diff --git a/docs/cookbook/openclaw-cloud-deployment.mdx b/docs/cookbook/openclaw-cloud-deployment.mdx
new file mode 100644
index 000000000..8dafac8cd
--- /dev/null
+++ b/docs/cookbook/openclaw-cloud-deployment.mdx
@@ -0,0 +1,317 @@
+---
+title: 'Deploy OpenClaw on AWS Lightsail'
+description: 'Set up your own AI personal assistant in the cloud using AWS Lightsail with Telegram integration'
+---
+
+[OpenClaw](https://github.com/openclaw/openclaw) is an open-source AI personal assistant that helps you manage tasks, answer questions, and automate workflows through messaging platforms. This guide walks you through deploying OpenClaw on AWS Lightsail and connecting it to Telegram.
+
+## What you'll need
+
+Before starting, make sure you have:
+
+- An [AWS account](https://aws.amazon.com/) (free tier eligible for some services)
+- A [Telegram account](https://telegram.org/)
+- An API key from either:
+ - [Anthropic](https://console.anthropic.com/) (for Claude models)
+ - [OpenAI](https://platform.openai.com/) (for GPT models)
+
+## Cost overview
+
+The recommended AWS Lightsail instance costs approximately **$20/month** for a 4GB RAM instance, which provides sufficient resources for running OpenClaw reliably.
+
+## Create your Lightsail instance
+
+### Step 1: Access AWS Lightsail
+
+1. Sign in to the [AWS Console](https://console.aws.amazon.com/)
+2. Search for "Lightsail" in the services search bar
+3. Click **Create instance**
+
+### Step 2: Configure the instance
+
+1. **Select your instance location**: Choose the AWS Region closest to you for better latency
+2. **Select a platform**: Choose **Linux/Unix**
+3. **Select a blueprint**: Choose **OS Only** → **Ubuntu 24.04 LTS**
+4. **Choose your instance plan**: Select the **$20 USD** plan (4 GB RAM, 2 vCPUs, 80 GB SSD)
+5. **Name your instance**: Enter a descriptive name like `openclaw-server`
+6. Click **Create instance**
+
+Wait for the instance status to show **Running** before proceeding.
+
+## Access your instance
+
+You have two options for connecting to your server.
+
+### Option 1: Browser-based SSH (easiest)
+
+1. In the Lightsail console, find your instance
+2. Click the terminal icon or **Connect using SSH**
+3. A browser-based terminal opens automatically
+
+### Option 2: SSH with keys (recommended for regular use)
+
+1. In Lightsail, go to **Account** → **SSH keys**
+2. Download your default key or create a new one
+3. Save the `.pem` file securely on your computer
+4. Set proper permissions and connect:
+
+```bash Terminal
+chmod 400 ~/Downloads/your-key.pem
+ssh -i ~/Downloads/your-key.pem ubuntu@YOUR_INSTANCE_IP
+```
+
+Replace `YOUR_INSTANCE_IP` with your instance's public IP address (found in the Lightsail console).
+
+## Install dependencies
+
+Once connected to your instance, update the system and install Node.js.
+
+### Update system packages
+
+```bash Terminal
+sudo apt update && sudo apt upgrade -y
+```
+
+### Install Node.js 22+
+
+OpenClaw requires Node.js version 22 or higher. Install it using NodeSource:
+
+```bash Terminal
+curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
+sudo apt install -y nodejs
+```
+
+### Verify installation
+
+```bash Terminal
+node --version
+npm --version
+```
+
+You should see Node.js version 22.x.x or higher.
+
+## Install and configure OpenClaw
+
+### Install OpenClaw globally
+
+```bash Terminal
+sudo npm install -g openclaw@latest
+```
+
+### Run the onboarding wizard
+
+OpenClaw includes an interactive setup wizard:
+
+```bash Terminal
+openclaw onboard
+```
+
+The wizard guides you through:
+- Choosing your AI provider (Anthropic or OpenAI)
+- Entering your API key
+- Configuring basic settings
+
+### Configure your AI provider
+
+After onboarding, you can customize your configuration. Edit the config file:
+
+```bash Terminal
+nano ~/.openclaw/openclaw.json
+```
+
+#### For Anthropic Claude
+
+```json Anthropic configuration
+{
+ "ai": {
+ "provider": "anthropic",
+ "model": "claude-sonnet-4-20250514",
+ "apiKey": "sk-ant-your-api-key-here"
+ }
+}
+```
+
+#### For OpenAI
+
+```json OpenAI configuration
+{
+ "ai": {
+ "provider": "openai",
+ "model": "gpt-4o",
+ "apiKey": "sk-your-api-key-here"
+ }
+}
+```
+
+Save the file with `Ctrl+O`, then exit with `Ctrl+X`.
+
+## Set up Telegram integration
+
+### Create a Telegram bot
+
+1. Open Telegram and search for [@BotFather](https://t.me/botfather)
+2. Start a chat and send `/newbot`
+3. Follow the prompts to:
+ - Choose a name for your bot (e.g., "My OpenClaw Assistant")
+ - Choose a username (must end in `bot`, e.g., `myopenclaw_bot`)
+4. BotFather responds with your **bot token** - save this securely
+
+### Configure OpenClaw for Telegram
+
+Add the Telegram channel to your OpenClaw configuration:
+
+```bash Terminal
+nano ~/.openclaw/openclaw.json
+```
+
+Update the configuration to include Telegram:
+
+```json Full configuration with Telegram
+{
+ "ai": {
+ "provider": "anthropic",
+ "model": "claude-sonnet-4-20250514",
+ "apiKey": "sk-ant-your-api-key-here"
+ },
+ "channels": {
+ "telegram": {
+ "enabled": true,
+ "token": "YOUR_TELEGRAM_BOT_TOKEN"
+ }
+ }
+}
+```
+
+Replace `YOUR_TELEGRAM_BOT_TOKEN` with the token from BotFather.
+
+## Security best practices
+
+### Configure the firewall
+
+Enable and configure Ubuntu's firewall (UFW):
+
+```bash Terminal
+sudo ufw allow OpenSSH
+sudo ufw enable
+sudo ufw status
+```
+
+### Protect your API keys
+
+Your API keys are stored in `~/.openclaw/openclaw.json`. Ensure proper permissions:
+
+```bash Terminal
+chmod 600 ~/.openclaw/openclaw.json
+```
+
+### Use SSH keys only
+
+For enhanced security, disable password authentication:
+
+1. Edit the SSH config:
+
+```bash Terminal
+sudo nano /etc/ssh/sshd_config
+```
+
+2. Find and set:
+
+```text sshd_config
+PasswordAuthentication no
+```
+
+3. Restart SSH:
+
+```bash Terminal
+sudo systemctl restart sshd
+```
+
+
+Make sure you can connect with your SSH key before disabling password authentication, or you may lock yourself out.
+
+
+For more security recommendations, see the [AWS Lightsail security best practices](https://docs.aws.amazon.com/lightsail/latest/userguide/understanding-ssh-in-amazon-lightsail.html).
+
+## Running OpenClaw
+
+### Start the agent
+
+Run OpenClaw to start your assistant:
+
+```bash Terminal
+openclaw start
+```
+
+OpenClaw connects to your configured channels (like Telegram) and starts listening for messages.
+
+### Test your setup
+
+1. Open Telegram and find your bot
+2. Send a message like "Hello!"
+3. Your OpenClaw assistant should respond
+
+### Usage pattern
+
+
+OpenClaw is designed for interactive usage rather than running 24/7. Start it when you need assistance and stop it when you're done. This approach is more cost-effective and secure.
+
+
+To stop OpenClaw, press `Ctrl+C` in the terminal.
+
+### Give instructions
+
+You can give your assistant instructions to customize its behavior:
+
+```bash Terminal
+openclaw instruct "You are my personal productivity assistant. Help me manage tasks and remind me of important deadlines."
+```
+
+## Troubleshooting
+
+### OpenClaw won't start
+
+**Check Node.js version:**
+
+```bash Terminal
+node --version
+```
+
+Ensure it's 22.x.x or higher.
+
+**Check your config file for syntax errors:**
+
+```bash Terminal
+cat ~/.openclaw/openclaw.json | python3 -m json.tool
+```
+
+### Telegram bot not responding
+
+- Verify your bot token is correct
+- Make sure you started the bot in Telegram (send `/start`)
+- Check that OpenClaw is running and shows the Telegram channel as connected
+
+### API errors
+
+- Verify your API key is valid and has available credits
+- Check you're using a supported model name
+- Ensure your API key has the correct permissions
+
+### Connection issues
+
+- Check your instance's public IP hasn't changed
+- Verify SSH is allowed through the firewall: `sudo ufw status`
+- Ensure your SSH key permissions are correct: `chmod 400 your-key.pem`
+
+## Next steps
+
+Now that OpenClaw is running, explore these options:
+
+- **Add more channels**: Configure additional messaging platforms like Discord or Slack
+- **Explore skills**: OpenClaw supports plugins for extended functionality
+- **Customize behavior**: Use the `openclaw instruct` command to fine-tune your assistant
+
+### Resources
+
+- [OpenClaw GitHub repository](https://github.com/openclaw/openclaw)
+- [OpenClaw documentation](https://openclaw.ai/)
+- [AWS Lightsail documentation](https://docs.aws.amazon.com/lightsail/)
diff --git a/docs/docs.json b/docs/docs.json
index 2e2ab01c4..78e74df79 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -9,14 +9,20 @@
},
"favicon": "/logo/favicon.png",
"contextual": {
- "options": ["copy", "chatgpt", "claude"]
+ "options": [
+ "copy",
+ "chatgpt",
+ "claude"
+ ]
},
"api": {
"playground": {
"display": "simple"
},
"examples": {
- "languages": ["javascript"]
+ "languages": [
+ "javascript"
+ ]
}
},
"navigation": {
@@ -26,14 +32,17 @@
"groups": [
{
"group": "Introduction",
- "pages": ["get-started/base"]
+ "pages": [
+ "get-started/base"
+ ]
},
{
"group": "Quickstart",
"pages": [
"get-started/build-app",
"get-started/launch-token",
- "get-started/deploy-smart-contracts"
+ "get-started/deploy-smart-contracts",
+ "get-started/learning-resources"
]
},
{
@@ -47,7 +56,11 @@
},
{
"group": "Build with AI",
- "pages": ["get-started/docs-mcp", "get-started/docs-llms", "get-started/prompt-library"]
+ "pages": [
+ "get-started/docs-mcp",
+ "get-started/docs-llms",
+ "get-started/prompt-library"
+ ]
}
],
"global": {
@@ -185,7 +198,9 @@
"groups": [
{
"group": "Introduction",
- "pages": ["base-account/overview/what-is-base-account"]
+ "pages": [
+ "base-account/overview/what-is-base-account"
+ ]
},
{
"group": "Quickstart",
@@ -420,7 +435,9 @@
"groups": [
{
"group": "Introduction",
- "pages": ["base-app/introduction/beta-faq"]
+ "pages": [
+ "base-app/introduction/beta-faq"
+ ]
},
{
"group": "Chat Agents",
@@ -537,15 +554,21 @@
"pages": [
{
"group": "Buy",
- "pages": ["onchainkit/latest/components/buy/buy"]
+ "pages": [
+ "onchainkit/latest/components/buy/buy"
+ ]
},
{
"group": "Checkout",
- "pages": ["onchainkit/latest/components/checkout/checkout"]
+ "pages": [
+ "onchainkit/latest/components/checkout/checkout"
+ ]
},
{
"group": "Earn",
- "pages": ["onchainkit/latest/components/earn/earn"]
+ "pages": [
+ "onchainkit/latest/components/earn/earn"
+ ]
},
{
"group": "Fund",
@@ -678,7 +701,9 @@
},
{
"group": "Token",
- "pages": ["onchainkit/latest/utilities/token/format-amount"]
+ "pages": [
+ "onchainkit/latest/utilities/token/format-amount"
+ ]
},
{
"group": "Wallet",
@@ -780,15 +805,21 @@
"pages": [
{
"group": "Buy",
- "pages": ["onchainkit/buy/buy"]
+ "pages": [
+ "onchainkit/buy/buy"
+ ]
},
{
"group": "Checkout",
- "pages": ["onchainkit/checkout/checkout"]
+ "pages": [
+ "onchainkit/checkout/checkout"
+ ]
},
{
"group": "Earn",
- "pages": ["onchainkit/earn/earn"]
+ "pages": [
+ "onchainkit/earn/earn"
+ ]
},
{
"group": "Fund",
@@ -869,11 +900,15 @@
},
{
"group": "Token",
- "pages": ["onchainkit/api/get-tokens"]
+ "pages": [
+ "onchainkit/api/get-tokens"
+ ]
},
{
"group": "Wallet",
- "pages": ["onchainkit/api/get-portfolios"]
+ "pages": [
+ "onchainkit/api/get-portfolios"
+ ]
}
]
},
@@ -933,7 +968,9 @@
},
{
"group": "Token",
- "pages": ["onchainkit/token/format-amount"]
+ "pages": [
+ "onchainkit/token/format-amount"
+ ]
},
{
"group": "Wallet",
@@ -1006,12 +1043,16 @@
"cookbook/defi-your-app",
"cookbook/go-gasless",
"cookbook/base-app-coins",
- "cookbook/testing-onchain-apps"
+ "cookbook/testing-onchain-apps",
+ "cookbook/openclaw-cloud-deployment"
]
},
{
"group": "Build with AI",
- "pages": ["cookbook/ai-prompting", "cookbook/base-builder-mcp"]
+ "pages": [
+ "cookbook/ai-prompting",
+ "cookbook/base-builder-mcp"
+ ]
},
{
"group": "Vibe Code a Mini App",
@@ -1058,394 +1099,8 @@
},
{
"tab": "Showcase",
- "pages": ["showcase"]
- },
- {
- "tab": "Learn",
- "groups": [
- {
- "group": "Building Onchain",
- "pages": ["learn/welcome"]
- },
- {
- "group": "Onchain Concepts",
- "pages": [
- "learn/onchain-concepts/core-concepts",
- "learn/onchain-concepts/understanding-the-onchain-tech-stack",
- {
- "group": "Web2 vs Building Onchain",
- "pages": [
- "learn/onchain-concepts/building-onchain-wallets",
- "learn/onchain-concepts/building-onchain-identity",
- "learn/onchain-concepts/building-onchain-gas",
- "learn/onchain-concepts/building-onchain-nodes",
- "learn/onchain-concepts/building-onchain-frontend-development",
- "learn/onchain-concepts/building-onchain-onramps",
- "learn/onchain-concepts/building-onchain-social-networks",
- "learn/onchain-concepts/building-onchain-ai"
- ]
- },
- "learn/onchain-concepts/development-flow"
- ]
- },
- {
- "group": "Introduction to Ethereum",
- "pages": [
- "learn/introduction-to-ethereum/introduction-to-ethereum-vid",
- "learn/introduction-to-ethereum/ethereum-dev-overview-vid",
- "learn/introduction-to-ethereum/ethereum-applications",
- "learn/introduction-to-ethereum/gas-use-in-eth-transactions",
- "learn/introduction-to-ethereum/evm-diagram",
- "learn/introduction-to-ethereum/guide-to-base"
- ]
- },
- {
- "group": "Onchain App Development",
- "pages": [
- {
- "group": "Frontend Setup",
- "pages": [
- "learn/onchain-app-development/frontend-setup/overview",
- "learn/onchain-app-development/frontend-setup/building-an-onchain-app",
- "learn/onchain-app-development/frontend-setup/wallet-connectors",
- "learn/onchain-app-development/frontend-setup/introduction-to-providers",
- "learn/onchain-app-development/frontend-setup/viem",
- "learn/onchain-app-development/frontend-setup/web3"
- ]
- },
- {
- "group": "Writing to Contracts",
- "pages": [
- "learn/onchain-app-development/writing-to-contracts/useWriteContract",
- "learn/onchain-app-development/writing-to-contracts/useSimulateContract"
- ]
- },
- {
- "group": "Reading and Displaying Data",
- "pages": [
- "learn/onchain-app-development/reading-and-displaying-data/useReadContract",
- "learn/onchain-app-development/reading-and-displaying-data/useAccount",
- "learn/onchain-app-development/reading-and-displaying-data/configuring-useReadContract"
- ]
- },
- {
- "group": "Account Abstraction",
- "pages": [
- "learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster",
- "learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-biconomy",
- "learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster",
- "learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-particle-network"
- ]
- },
- {
- "group": "Cross-Chain Development",
- "pages": [
- "learn/onchain-app-development/cross-chain/bridge-tokens-with-layerzero",
- "learn/onchain-app-development/cross-chain/send-messages-and-tokens-from-base-chainlink"
- ]
- },
- {
- "group": "Finance",
- "pages": [
- "learn/onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds",
- "learn/onchain-app-development/finance/access-real-world-data-chainlink",
- "learn/onchain-app-development/finance/build-a-smart-wallet-funding-app"
- ]
- },
- {
- "group": "Deploy with Fleek",
- "pages": ["learn/onchain-app-development/deploy-with-fleek"]
- }
- ]
- },
- {
- "group": "Smart Contract Development",
- "pages": [
- {
- "group": "Introduction to Solidity",
- "pages": [
- "learn/introduction-to-solidity/introduction-to-solidity-overview",
- "learn/introduction-to-solidity/anatomy-of-a-smart-contract-vid",
- {
- "group": "Introduction to Solidity",
- "pages": [
- "learn/introduction-to-solidity/introduction-to-solidity-vid",
- "learn/introduction-to-solidity/solidity-overview",
- "learn/introduction-to-solidity/introduction-to-remix-vid",
- "learn/introduction-to-solidity/introduction-to-remix",
- "learn/introduction-to-solidity/deployment-in-remix-vid",
- "learn/introduction-to-solidity/deployment-in-remix"
- ]
- }
- ]
- },
- {
- "group": "Contracts and Basic Functions",
- "pages": [
- "learn/contracts-and-basic-functions/intro-to-contracts-vid",
- "learn/contracts-and-basic-functions/hello-world-step-by-step",
- "learn/contracts-and-basic-functions/basic-types",
- "learn/contracts-and-basic-functions/basic-functions-exercise"
- ]
- },
- {
- "group": "Deploying to a Testnet",
- "pages": [
- "learn/deployment-to-testnet/overview-of-test-networks-vid",
- "learn/deployment-to-testnet/test-networks",
- "learn/deployment-to-testnet/deployment-to-base-sepolia-sbs",
- "learn/deployment-to-testnet/contract-verification-sbs",
- "learn/deployment-to-testnet/deployment-to-testnet-exercise"
- ]
- },
- {
- "group": "Control Structures",
- "pages": [
- "learn/control-structures/standard-control-structures-vid",
- "learn/control-structures/loops-vid",
- "learn/control-structures/require-revert-error-vid",
- "learn/control-structures/control-structures",
- "learn/control-structures/control-structures-exercise"
- ]
- },
- {
- "group": "Storage in Solidity",
- "pages": [
- "learn/storage/simple-storage-video",
- "learn/storage/simple-storage-sbs",
- "learn/storage/how-storage-works-video",
- "learn/storage/how-storage-works",
- "learn/storage/storage-exercise"
- ]
- },
- {
- "group": "Arrays in Solidity",
- "pages": [
- "learn/arrays/arrays-in-solidity-vid",
- "learn/arrays/writing-arrays-in-solidity-vid",
- "learn/arrays/arrays-in-solidity",
- "learn/arrays/filtering-an-array-sbs",
- "learn/arrays/fixed-size-arrays-vid",
- "learn/arrays/array-storage-layout-vid",
- "learn/arrays/arrays-exercise"
- ]
- },
- {
- "group": "The Mapping Type",
- "pages": [
- "learn/mappings/mappings-vid",
- "learn/mappings/using-msg-sender-vid",
- "learn/mappings/mappings-sbs",
- "learn/mappings/how-mappings-are-stored-vid",
- "learn/mappings/mappings-exercise"
- ]
- },
- {
- "group": "Advanced Functions",
- "pages": [
- "learn/advanced-functions/function-visibility-vid",
- "learn/advanced-functions/function-visibility",
- "learn/advanced-functions/function-modifiers-vid",
- "learn/advanced-functions/function-modifiers"
- ]
- },
- {
- "group": "Structs",
- "pages": [
- "learn/structs/structs-vid",
- "learn/structs/structs-sbs",
- "learn/structs/structs-exercise"
- ]
- },
- {
- "group": "Inheritance",
- "pages": [
- "learn/inheritance/inheritance-vid",
- "learn/inheritance/inheritance-sbs",
- "learn/inheritance/multiple-inheritance-vid",
- "learn/inheritance/multiple-inheritance",
- "learn/inheritance/abstract-contracts-vid",
- "learn/inheritance/abstract-contracts-sbs",
- "learn/inheritance/inheritance-exercise"
- ]
- },
- {
- "group": "Imports",
- "pages": [
- "learn/imports/imports-vid",
- "learn/imports/imports-sbs",
- "learn/imports/imports-exercise"
- ]
- },
- {
- "group": "Errors",
- "pages": [
- "learn/error-triage/error-triage-vid",
- "learn/error-triage/error-triage",
- "learn/error-triage/error-triage-exercise"
- ]
- },
- {
- "group": "The new Keyword",
- "pages": [
- "learn/new-keyword/creating-a-new-contract-vid",
- "learn/new-keyword/new-keyword-sbs",
- "learn/new-keyword/new-keyword-exercise"
- ]
- },
- {
- "group": "Contract to Contract Interactions",
- "pages": [
- "learn/interfaces/intro-to-interfaces-vid",
- "learn/interfaces/calling-another-contract-vid",
- "learn/interfaces/testing-the-interface-vid",
- "learn/interfaces/contract-to-contract-interaction"
- ]
- },
- {
- "group": "Events",
- "pages": ["learn/events/hardhat-events-sbs"]
- },
- {
- "group": "Address and Payable",
- "pages": ["learn/address-and-payable/address-and-payable"]
- }
- ]
- },
- {
- "group": "Development with Foundry",
- "pages": [
- "learn/foundry/deploy-with-foundry",
- "learn/foundry/setup-with-base",
- "learn/foundry/testing-smart-contracts",
- "learn/foundry/verify-contract-with-basescan",
- "learn/foundry/generate-random-numbers-contracts"
- ]
- },
- {
- "group": "Development with Hardhat",
- "pages": [
- {
- "group": "Hardhat Setup and Overview",
- "pages": [
- "learn/hardhat/hardhat-setup-overview/hardhat-overview-vid",
- "learn/hardhat/hardhat-setup-overview/creating-a-project-vid",
- "learn/hardhat/hardhat-setup-overview/hardhat-setup-overview-sbs"
- ]
- },
- {
- "group": "Testing with Typescript",
- "pages": [
- "learn/hardhat/hardhat-testing/testing-overview-vid",
- "learn/hardhat/hardhat-testing/writing-tests-vid",
- "learn/hardhat/hardhat-testing/contract-abi-and-testing-vid",
- "learn/hardhat/hardhat-testing/hardhat-testing-sbs"
- ]
- },
- {
- "group": "Etherscan",
- "pages": [
- "learn/hardhat/etherscan/etherscan-sbs",
- "learn/hardhat/etherscan/etherscan-vid"
- ]
- },
- {
- "group": "Deploying Smart Contracts",
- "pages": [
- "learn/hardhat/hardhat-deploy/installing-hardhat-deploy-vid",
- "learn/hardhat/hardhat-deploy/setup-deploy-script-vid",
- "learn/hardhat/hardhat-deploy/testing-our-deployment-vid",
- "learn/hardhat/hardhat-deploy/test-network-configuration-vid",
- "learn/hardhat/hardhat-deploy/deployment-vid",
- "learn/hardhat/hardhat-deploy/hardhat-deploy-sbs"
- ]
- },
- {
- "group": "Verifying Smart Contracts",
- "pages": [
- "learn/hardhat/hardhat-verify/hardhat-verify-vid",
- "learn/hardhat/hardhat-verify/hardhat-verify-sbs"
- ]
- },
- {
- "group": "Mainnet Forking",
- "pages": [
- "learn/hardhat/hardhat-forking/mainnet-forking-vid",
- "learn/hardhat/hardhat-forking/hardhat-forking"
- ]
- },
- {
- "group": "Hardhat Tools and Testing",
- "pages": [
- "learn/hardhat/hardhat-tools-and-testing/overview",
- "learn/hardhat/hardhat-tools-and-testing/debugging-smart-contracts",
- "learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage",
- "learn/hardhat/hardhat-tools-and-testing/optimizing-gas-usage",
- "learn/hardhat/hardhat-tools-and-testing/reducing-contract-size",
- "learn/hardhat/hardhat-tools-and-testing/deploy-with-hardhat"
- ]
- }
- ]
- },
- {
- "group": "Token Development",
- "pages": [
- {
- "group": "Introduction to Tokens",
- "pages": [
- "learn/token-development/intro-to-tokens/intro-to-tokens-vid",
- "learn/token-development/intro-to-tokens/misconceptions-about-tokens-vid",
- "learn/token-development/intro-to-tokens/tokens-overview"
- ]
- },
- {
- "group": "Minimal Tokens",
- "pages": [
- "learn/token-development/minimal-tokens/creating-a-minimal-token-vid",
- "learn/token-development/minimal-tokens/transferring-a-minimal-token-vid",
- "learn/token-development/minimal-tokens/minimal-token-sbs",
- "learn/token-development/minimal-tokens/minimal-tokens-exercise"
- ]
- },
- {
- "group": "ERC-20 Tokens",
- "pages": [
- "learn/token-development/erc-20-token/analyzing-erc-20-vid",
- "learn/token-development/erc-20-token/erc-20-standard",
- "learn/token-development/erc-20-token/openzeppelin-erc-20-vid",
- "learn/token-development/erc-20-token/erc-20-testing-vid",
- "learn/token-development/erc-20-token/erc-20-token-sbs",
- "learn/token-development/erc-20-token/erc-20-exercise"
- ]
- },
- {
- "group": "ERC-721 Tokens",
- "pages": [
- "learn/token-development/erc-721-token/erc-721-standard-video",
- "learn/token-development/erc-721-token/erc-721-standard",
- "learn/token-development/erc-721-token/erc-721-on-opensea-vid",
- "learn/token-development/erc-721-token/openzeppelin-erc-721-vid",
- "learn/token-development/erc-721-token/implementing-an-erc-721-vid",
- "learn/token-development/erc-721-token/erc-721-sbs",
- "learn/token-development/erc-721-token/erc-721-exercise"
- ]
- },
- {
- "group": "NFT Guides",
- "pages": [
- "learn/token-development/nft-guides/signature-mint",
- "learn/token-development/nft-guides/dynamic-nfts",
- "learn/token-development/nft-guides/complex-onchain-nfts",
- "learn/token-development/nft-guides/simple-onchain-nfts",
- "learn/token-development/nft-guides/thirdweb-unreal-nft-items"
- ]
- }
- ]
- },
- {
- "group": "Exercise Contracts",
- "pages": ["learn/exercise-contracts"]
- }
+ "pages": [
+ "showcase"
]
}
]
@@ -1885,54 +1540,6 @@
"source": "/chain/why-base",
"destination": "/base-chain/quickstart/why-base"
},
- {
- "source": "/cookbook/account-abstraction/account-abstraction-on-base-using-biconomy",
- "destination": "/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-biconomy"
- },
- {
- "source": "/cookbook/account-abstraction/account-abstraction-on-base-using-particle-network",
- "destination": "/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-particle-network"
- },
- {
- "source": "/cookbook/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster",
- "destination": "/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster"
- },
- {
- "source": "/cookbook/account-abstraction/gasless-transactions-with-paymaster",
- "destination": "/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster"
- },
- {
- "source": "/cookbook/client-side-development/introduction-to-providers",
- "destination": "/learn/onchain-app-development/frontend-setup/introduction-to-providers"
- },
- {
- "source": "/cookbook/client-side-development/viem",
- "destination": "/learn/onchain-app-development/frontend-setup/viem"
- },
- {
- "source": "/cookbook/client-side-development/web3",
- "destination": "/learn/onchain-app-development/frontend-setup/web3"
- },
- {
- "source": "/cookbook/cross-chain/bridge-tokens-with-layerzero",
- "destination": "/learn/onchain-app-development/cross-chain/bridge-tokens-with-layerzero"
- },
- {
- "source": "/cookbook/cross-chain/send-messages-and-tokens-from-base-chainlink",
- "destination": "/learn/onchain-app-development/cross-chain/send-messages-and-tokens-from-base-chainlink"
- },
- {
- "source": "/cookbook/defi/access-real-time-asset-data",
- "destination": "/learn/onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds"
- },
- {
- "source": "/cookbook/defi/access-real-world-data",
- "destination": "/learn/onchain-app-development/finance/access-real-world-data-chainlink"
- },
- {
- "source": "/cookbook/defi/add-in-app-funding",
- "destination": "/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app"
- },
{
"source": "/cookbook/growth/cast-actions",
"destination": "/cookbook/onchain-social"
@@ -1957,58 +1564,14 @@
"source": "/cookbook/growth/retaining-users",
"destination": "/cookbook/onchain-social"
},
- {
- "source": "/cookbook/ipfs/deploy-with-fleek",
- "destination": "/learn/onchain-app-development/deploy-with-fleek"
- },
- {
- "source": "/cookbook/nfts/complex-onchain-nfts",
- "destination": "/learn/token-development/nft-guides/complex-onchain-nfts"
- },
- {
- "source": "/cookbook/nfts/dynamic-nfts",
- "destination": "/learn/token-development/nft-guides/dynamic-nfts"
- },
- {
- "source": "/cookbook/nfts/nft-minting-zora",
- "destination": "/learn/token-development/intro-to-tokens/intro-to-tokens-vid"
- },
- {
- "source": "/cookbook/nfts/signature-mint",
- "destination": "/learn/token-development/nft-guides/signature-mint"
- },
- {
- "source": "/cookbook/nfts/simple-onchain-nfts",
- "destination": "/learn/token-development/nft-guides/simple-onchain-nfts"
- },
- {
- "source": "/cookbook/nfts/thirdweb-unreal-nft-items",
- "destination": "/learn/token-development/nft-guides/thirdweb-unreal-nft-items"
- },
{
"source": "/cookbook/payments/build-ecommerce-app",
"destination": "/onchainkit/checkout/checkout"
},
- {
- "source": "/cookbook/payments/deploy-shopify-storefront",
- "destination": "/learn/welcome"
- },
{
"source": "/cookbook/payments/transaction-guide",
"destination": "/cookbook/defi-your-app"
},
- {
- "source": "/cookbook/smart-contract-development/foundry/:slug*",
- "destination": "/learn/foundry/:slug*"
- },
- {
- "source": "/cookbook/smart-contract-development/hardhat/:slug*",
- "destination": "/learn/hardhat/hardhat-tools-and-testing/:slug*"
- },
- {
- "source": "/cookbook/smart-contract-development/remix/:slug*",
- "destination": "/learn/introduction-to-solidity/deployment-in-remix"
- },
{
"source": "/cookbook/social/convert-farcaster-frame",
"destination": "/cookbook/onchain-social"
@@ -2021,10 +1584,6 @@
"source": "/cookbook/social/farcaster-no-code-nft-minting",
"destination": "/cookbook/onchain-social"
},
- {
- "source": "/cookbook/token-gating/gate-irl-events-with-nouns",
- "destination": "/learn/welcome"
- },
{
"source": "/cookbook/use-case-guides/cast-actions",
"destination": "/cookbook/onchain-social"
@@ -2033,10 +1592,6 @@
"source": "/cookbook/use-case-guides/commerce/build-an-ecommerce-app",
"destination": "/onchainkit/checkout/checkout"
},
- {
- "source": "/cookbook/use-case-guides/commerce/deploy-a-shopify-storefront",
- "destination": "/learn/welcome"
- },
{
"source": "/cookbook/use-case-guides/create-email-campaigns",
"destination": "/cookbook/onchain-social"
@@ -2045,26 +1600,10 @@
"source": "/cookbook/use-case-guides/creator/convert-farcaster-frame-to-open-frame",
"destination": "/cookbook/onchain-social"
},
- {
- "source": "/cookbook/use-case-guides/creator/nft-minting-with-zora",
- "destination": "/learn/token-development/intro-to-tokens/intro-to-tokens-vid"
- },
{
"source": "/cookbook/use-case-guides/deploy-to-vercel",
"destination": "/base-app/build-with-minikit/quickstart#deploying-to-vercel"
},
- {
- "source": "/cookbook/use-case-guides/finance/access-real-time-asset-data-pyth-price-feeds",
- "destination": "/learn/onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds"
- },
- {
- "source": "/cookbook/use-case-guides/finance/access-real-world-data-chainlink",
- "destination": "/learn/onchain-app-development/finance/access-real-world-data-chainlink"
- },
- {
- "source": "/cookbook/use-case-guides/finance/build-a-smart-wallet-funding-app",
- "destination": "/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app"
- },
{
"source": "/cookbook/use-case-guides/gating-and-redirects",
"destination": "/cookbook/onchain-social"
@@ -2445,142 +1984,6 @@
"source": "/smart-wallet/:slug*",
"destination": "/base-account/:slug*"
},
- {
- "source": "/learn/account-abstraction",
- "destination": "/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster"
- },
- {
- "source": "/learn/client-side-development",
- "destination": "/learn/onchain-app-development/frontend-setup/introduction-to-providers"
- },
- {
- "source": "/learn/cross-chain-development",
- "destination": "/learn/onchain-app-development/cross-chain/bridge-tokens-with-layerzero"
- },
- {
- "source": "/learn/deploy-with-fleek",
- "destination": "/learn/onchain-app-development/deploy-with-fleek"
- },
- {
- "source": "/learn/development-tools/overview",
- "destination": "/learn/welcome"
- },
- {
- "source": "/learn/erc-20-token/:slug*",
- "destination": "/learn/token-development/erc-20-token/:slug*"
- },
- {
- "source": "/learn/erc-721-token/:slug*",
- "destination": "/learn/token-development/erc-721-token/:slug*"
- },
- {
- "source": "/learn/ethereum-applications",
- "destination": "/learn/introduction-to-ethereum/ethereum-applications"
- },
- {
- "source": "/learn/ethereum-dev-overview",
- "destination": "/learn/introduction-to-ethereum/ethereum-dev-overview-vid"
- },
- {
- "source": "/learn/etherscan/:slug*",
- "destination": "/learn/hardhat/etherscan/:slug*"
- },
- {
- "source": "/learn/evm-diagram",
- "destination": "/learn/introduction-to-ethereum/evm-diagram"
- },
- {
- "source": "/learn/frontend-setup/:slug*",
- "destination": "/learn/onchain-app-development/frontend-setup/:slug*"
- },
- {
- "source": "/learn/gas-use-in-eth-transactions",
- "destination": "/learn/introduction-to-ethereum/gas-use-in-eth-transactions"
- },
- {
- "source": "/learn/guide-to-base",
- "destination": "/learn/introduction-to-ethereum/guide-to-base"
- },
- {
- "source": "/learn/hardhat-deploy/:slug*",
- "destination": "/learn/hardhat/hardhat-deploy/:slug*"
- },
- {
- "source": "/learn/hardhat-forking/:slug*",
- "destination": "/learn/hardhat/hardhat-forking/:slug*"
- },
- {
- "source": "/learn/hardhat-setup-overview/:slug*",
- "destination": "/learn/hardhat/hardhat-setup-overview/:slug*"
- },
- {
- "source": "/learn/hardhat-testing/:slug*",
- "destination": "/learn/hardhat/hardhat-testing/:slug*"
- },
- {
- "source": "/learn/hardhat-tools-and-testing/overview",
- "destination": "/learn/hardhat/hardhat-tools-and-testing/overview"
- },
- {
- "source": "/learn/hardhat-verify/:slug*",
- "destination": "/learn/hardhat/hardhat-verify/:slug*"
- },
- {
- "source": "/learn/help-on-discord",
- "destination": "/learn/welcome"
- },
- {
- "source": "/learn/intro-to-tokens/:slug*",
- "destination": "/learn/token-development/intro-to-tokens/:slug*"
- },
- {
- "source": "/learn/introduction-to-ethereum",
- "destination": "/learn/introduction-to-ethereum/introduction-to-ethereum-vid"
- },
- {
- "source": "/learn/learning-objectives",
- "destination": "/learn/welcome"
- },
- {
- "source": "/learn/minimal-tokens/:slug*",
- "destination": "/learn/token-development/minimal-tokens/:slug*"
- },
- {
- "source": "/learn/reading-and-displaying-data/:slug*",
- "destination": "/learn/onchain-app-development/reading-and-displaying-data/:slug*"
- },
- {
- "source": "/learn/writing-to-contracts/:slug*",
- "destination": "/learn/onchain-app-development/writing-to-contracts/:slug*"
- },
- {
- "source": "/base-learn/progress",
- "destination": "/learn/welcome"
- },
- {
- "source": "/tutorials/intro-to-foundry-setup",
- "destination": "/learn/foundry/deploy-with-foundry"
- },
- {
- "source": "/tutorials/hardhat-profiling-gas",
- "destination": "/learn/hardhat/hardhat-tools-and-testing/optimizing-gas-usage"
- },
- {
- "source": "/tutorials/hardhat-profiling-size",
- "destination": "/learn/hardhat/hardhat-tools-and-testing/reducing-contract-size"
- },
- {
- "source": "/tutorials/hardhat-debugging",
- "destination": "/learn/hardhat/hardhat-tools-and-testing/debugging-smart-contracts"
- },
- {
- "source": "/tutorials/hardhat-test-coverage",
- "destination": "/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage"
- },
- {
- "source": "/tutorials/intro-to-providers",
- "destination": "/learn/onchain-app-development/frontend-setup/introduction-to-providers"
- },
{
"source": "/network-information",
"destination": "/base-chain/quickstart/connecting-to-base"
@@ -2593,10 +1996,6 @@
"source": "/tools/network-faucets",
"destination": "/base-chain/tools/network-faucets"
},
- {
- "source": "/tutorials",
- "destination": "/learn"
- },
{
"source": "/tutorials/deploy-with-foundry",
"destination": "/cookbook/smart-contract-development/foundry/deploy-with-foundry"
@@ -2844,6 +2243,10 @@
{
"source": "/mini-apps/technical-reference/minikit/hooks/useNotification",
"destination": "/onchainkit/latest/components/minikit/hooks/useNotification"
+ },
+ {
+ "source": "/learn/:slug*",
+ "destination": "/get-started/learning-resources"
}
],
"integrations": {
diff --git a/docs/get-started/learning-resources.mdx b/docs/get-started/learning-resources.mdx
new file mode 100644
index 000000000..da873366e
--- /dev/null
+++ b/docs/get-started/learning-resources.mdx
@@ -0,0 +1,20 @@
+---
+title: "Learning Resources"
+description: "Find educational content for learning Solidity, Ethereum, and blockchain development"
+---
+
+## Archived Content
+
+All previous learning content, including Solidity tutorials, Ethereum basics, and blockchain development guides, is now available at:
+
+**[github.com/base/learn-docs](https://github.com/base/learn-docs)**
+
+## Coming Soon
+
+We will be adding more learning resources to help you build on Base. Stay tuned for:
+
+- Updated tutorials and guides
+- Community-contributed resources
+- New educational content
+
+In the meantime, check out the [Cookbook](/cookbook/onchain-social) for practical tutorials and implementation guides.
diff --git a/docs/learn/address-and-payable/address-and-payable.mdx b/docs/learn/address-and-payable/address-and-payable.mdx
deleted file mode 100644
index 1878e6acf..000000000
--- a/docs/learn/address-and-payable/address-and-payable.mdx
+++ /dev/null
@@ -1,91 +0,0 @@
----
-title: Address and Payable in Solidity
-sidebarTitle: Guide
-description: A comprehensive guide to understanding and using address and payable address types in Solidity.
-hide_table_of_contents: false
----
-
-Understanding address and payable address types is crucial for managing Ether transfers and interactions within your Solidity contracts. This article will delve into their key distinctions and practical applications.
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- Differentiate between address and address payable types in Solidity
-- Determine when to use each type appropriately in contract development
-- Employ address payable to send Ether and interact with payable functions
-
----
-
-## Ethereum Addresses
-
-In Solidity, Ethereum addresses play a crucial role in interacting with the Ethereum blockchain. An Ethereum address is a 20-byte hexadecimal string that represents the destination of transactions or the owner of a smart contract. These addresses are used to send and receive Ether and interact with smart contracts.
-
-### Addresses
-
-Regular addresses in Solidity are used for various purposes, including:
-
-- Identifying the owner of a smart contract
-- Sending Ether from one address to another
-- Checking the balance of an address
- Here's an example of declaring a regular address variable in Solidity:
-
-
-
-```solidity
-address public owner;
-```
-
-### Payable Addresses
-
-`payable` keyword is a language-level feature provided by Solidity to enable the handling of Ether within smart contracts, and it is not a feature of the Ethereum Virtual Machine itself, but rather a part of the Solidity language's syntax. They are used when you want a contract to be able to receive Ether from external sources, such as other contracts or user accounts.
-
-Payable addresses are often used when creating crowdfunding or token sale contracts, where users send Ether to the contract's address in exchange for tokens or to fund a project.
-
-Here's an example of declaring a payable address variable in Solidity:
-
-```solidity
-address payable public projectWallet;
-```
-
-Payable [Address] are marked as payable, which means they can accept incoming Ether transactions. It's important to note that regular addresses cannot receive Ether directly.
-
-## Receiving Ether with Payable Addresses
-
-To receive Ether in a contract using a payable address, you need to define a payable function that can accept incoming transactions. This function is typically named receive or fallback. Here's an example:
-
-```solidity
-fallback() external payable {
- // Handle the incoming Ether here
-}
-```
-
-In this example, the fallback function is marked as external and payable, which means it can receive Ether when someone sends it to the contract's address. You can then add custom logic to handle the received Ether, such as updating contract balances or triggering specific actions.
-
-## Usage
-
-```solidity
-contract PaymentReceiver {
- address payable owner;
-
- constructor() payable {
- owner = payable(msg.sender); // Convert msg.sender to payable
- }
-
- function receiveEther() public payable {
- // This function can receive Ether
- }
-
- function withdrawEther() public {
- owner.transfer(address(this).balance); // Send Ether to owner
- }
-}
-```
-
-## Conclusion
-
-Appropriately using address and address payable types is essential for secure and efficient Solidity contract development. By understanding their distinctions and applying them correctly, you can effectively manage Ether transfers and interactions within your contracts.
-
-[Address]: https://docs.soliditylang.org/en/latest/types.html#address
diff --git a/docs/learn/advanced-functions/function-modifiers-vid.mdx b/docs/learn/advanced-functions/function-modifiers-vid.mdx
deleted file mode 100644
index 21a9fa35f..000000000
--- a/docs/learn/advanced-functions/function-modifiers-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Function Modifiers
-description: Use modifiers to control how functions work.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/advanced-functions/function-modifiers.mdx b/docs/learn/advanced-functions/function-modifiers.mdx
deleted file mode 100644
index c92a68940..000000000
--- a/docs/learn/advanced-functions/function-modifiers.mdx
+++ /dev/null
@@ -1,151 +0,0 @@
----
-title: Function Modifiers
-sidebarTitle: Modifiers Guide
-description: Build custom function modifiers to efficiently modify functionality.
-hide_table_of_contents: false
----
-
-Function modifiers allow you to efficiently change the behavior of functions. In some ways, it's similar to inheritance, but there are restrictions, particularly in variable scope.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Use modifiers to efficiently add functionality to multiple functions
-
----
-
-## Adding a Simple OnlyOwner Modifier
-
-By default, `public` functions can be called by **anyone**, without restriction. Often this is desirable. You want any user to be able to see what NFTs are for sale on your platform, sign up for a service, or read various items stored in state.
-
-However, there will be many functions you **don't** want any user to be able to do, such as setting the fee for using the app, or withdrawing all funds in the contract! A common pattern to protect these functions is to use `modifier`s to make sure that only the owner can call these functions.
-
-
-For a production app, you'll want to use a more robust implementation of `onlyOwner`, such as the [one provided by OpenZeppelin].
-
-
-
-### Adding an Owner
-
-The address of the deployer of a contract is **not** included as an accessible property. To make it available, add it as a state variable and assign `msg.sender` in the `constructor`.
-
-
-```solidity
-contract Modifiers {
- address owner;
-
- constructor () {
- owner = msg.sender;
- }
-}
-```
-
-
-
-### Creating an `onlyOwner` Modifier
-
-[Modifiers] are very similar to functions and are declared with the `modifier` keyword. The modifier can run any Solidity code, including functions, and is allowed to modify state. Modifiers must have a special `_` character, which serves as a placeholder for where the code contained within the modified function will run.
-
-Create a simple `onlyOwner` modifier, which returns an `error` of `NotOwner` with the sending address if the sender is not the owner.
-
-
-```solidity
-error NotOwner(address _msgSender);
-```
-
-```solidity
-modifier onlyOwner {
- if (msg.sender != owner) {
- revert NotOwner(msg.sender);
- }
- _;
-}
-```
-
-
-Test your `modifier` by adding a function that uses it:
-
-
-```solidity
-function iOwnThis() public view onlyOwner returns (string memory) {
- return "You own this!";
-}
-```
-
-
-To test, deploy your contract and call the `iOwnThis` function. You should see the message "You own this!".
-
-Next, switch the _Account_, and try the function again. You should see an error in the console:
-
-```text
-call to Modifiers.iOwnThis errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-Error provided by the contract:
-NotOwner
-Parameters:
-{
- "_msgSender": {
- "value": "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db"
- }
-}
-Debug the transaction to get more information.
-```
-
-
-Always verify the output of a function call in the console. The result that appears under the button for the function is convenient, but it does **not** clear or change if a subsequent call reverts.
-
-
-
----
-
-## Modifiers and Variables
-
-Modifiers can have parameters, which essentially work the same as in functions. These parameters can be independent values, or they can overlap with the arguments provided to a function call.
-
-### Modifiers with Parameters
-
-Modifier parameters can be the arguments provided to the functions they modify. You can perform calculations and trigger errors based on these values.
-
-```solidity
-error NotEven(uint number);
-
-modifier onlyEven(uint _number) {
- if(_number % 2 != 0) {
- revert NotEven(_number);
- }
- _;
-}
-
-function halver(uint _number) public pure onlyEven(_number) returns (uint) {
- return _number / 2;
-}
-```
-
-### Independent Scope
-
-While `modifiers` are used to modify functions and can share inputs, they have separate scopes. The following example will **not** work:
-
-```solidity
-// Bad code example, does not work
-modifier doubler(uint _number) {
- _number *= 2;
- _;
-}
-
-function modifierDoubler(uint _number) public pure doubler(_number) returns (uint) {
- return _number; // Returns the original number, NOT number * 2
-}
-```
-
----
-
-## Conclusion
-
-Function `modifier`s are an efficient and reusable way to add checks, trigger errors, and control function execution. In this lesson, you've seen examples of how they can be used to abort execution under certain conditions. You've also learned that they have separate scopes and cannot be used to modify variables within the function they modify.
-
-[one provided by OpenZeppelin]: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
diff --git a/docs/learn/advanced-functions/function-visibility-vid.mdx b/docs/learn/advanced-functions/function-visibility-vid.mdx
deleted file mode 100644
index d6beda135..000000000
--- a/docs/learn/advanced-functions/function-visibility-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Function Visibility
-description: Learn how to control the visibility of functions.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/advanced-functions/function-visibility.mdx b/docs/learn/advanced-functions/function-visibility.mdx
deleted file mode 100644
index 628f0504d..000000000
--- a/docs/learn/advanced-functions/function-visibility.mdx
+++ /dev/null
@@ -1,131 +0,0 @@
----
-title: Function Visibility and State Mutability
-sidebarTitle: Visibility Overview
-description: A quick reference for all your function declaring needs.
-hide_table_of_contents: false
----
-
-You've seen much of this before, but this document outlines and highlights the options for _function visibility_ and _state mutability_ all in one document.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Categorize functions as public, private, internal, or external based on their usage
-- Describe how pure and view functions are different than functions that modify storage
-
----
-
-## Function Visibility
-
-There are four types of [visibility] for functions in Solidity: `external`, `public`, `internal`, and `private`. These labels represent a further division of the _public_ and _private_ labels you might use in another language.
-
-### External
-
-Functions with `external` visibility are **only** callable from other contracts and cannot be called within their own contract. You may see older references stating you should use `external` over `public` because it forces the function to use `calldata`. This is no longer correct, because both function visibilities can now use `calldata` and `memory` for parameters. However, using `calldata` for either will cost less gas.
-
-```solidity
-contract Foo {
- constructor() {
- // Bad code example, will not work
- uint bar = foo(3);
- // ... other code
- }
-
- function foo(uint _number) external pure returns (uint) {
- return _number*2;
- }
-}
-```
-
-### Public
-
-Public functions work the same as external, except they may also be called within the contract that contains them.
-
-```solidity
-contract Foo {
- constructor() {
- // Public functions may be called within the contract
- uint bar = foo(3);
- // ... other code
- }
-
- function foo(uint _number) public pure returns (uint) {
- return _number*2;
- }
-}
-```
-
-### Private and Internal
-
-Functions visible as `private` and `internal` operate nearly identically. Beyond writing hygienic code, these have a very important effect. Because they are not a part of the contract's ABI, you can use `mapping`s and `storage` variable references as parameters.
-
-The difference is that `private` functions can't be called from derived contracts. You'll learn more about that when we cover inheritance.
-
-Some developers prepend an underscore to `private` and `internal` functions.
-
-```solidity
-function _foo(uint _number) private returns (uint) {
- return _number*2;
-}
-```
-
-
-All data on a blockchain is public. Don't mistake hiding visibility while coding for hiding information from the world!
-
-
-
----
-
-## Function State Mutability
-
-State mutability labels are relatively unique to Solidity. They determine how a function can interact with state, which has a substantial impact on gas costs.
-
-### Pure
-
-`pure` functions promise to neither read nor write state. They're usually used for helper functions that support other functionality.
-
-```solidity
-function abs(int x) public pure returns (int) {
- return x >= 0 ? x : -x;
-}
-```
-
-`pure` functions can be called from outside the blockchain without using gas, if they are also `public` or `external`.
-
-### View
-
-`view` functions access state, but don't modify it. You've used these for tasks such as returning all the values in an array.
-
-```solidity
-function getArr() public view returns (uint[] memory) {
- return arr;
-}
-```
-
-`view` functions can be called from outside the blockchain without using gas, if they are also `public` or `external`.
-
-### Unlabeled Functions
-
-Functions that are not labeled `view` or `pure` can modify state and the compiler will generate a warning if they do not.
-
-```solidity
-function addToArr(uint _number) public {
- arr.push(_number);
-}
-```
-
-They can have any visibility and will always cost gas when called.
-
----
-
-## Conclusion
-
-The visibility and mutability keywords in Solidity help you organize your code and alert other developers to the properties of each of your functions. Use them to keep your code organized and readable.
-
----
-
-[visibility]: https://docs.soliditylang.org/en/v0.8.17/contracts.html?highlight=pure#function-visibility
-
diff --git a/docs/learn/arrays/array-storage-layout-vid.mdx b/docs/learn/arrays/array-storage-layout-vid.mdx
deleted file mode 100644
index 26724cf11..000000000
--- a/docs/learn/arrays/array-storage-layout-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Array Storage Layout
-description: Learn how arrays are kept in storage.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/arrays/arrays-exercise.mdx b/docs/learn/arrays/arrays-exercise.mdx
deleted file mode 100644
index 1c9b96e35..000000000
--- a/docs/learn/arrays/arrays-exercise.mdx
+++ /dev/null
@@ -1,93 +0,0 @@
----
-title: Arrays Exercise
-sidebarTitle: Exercise
-description: Exercise - Demonstrate your knowledge of arrays.
-hide_table_of_contents: false
----
-
-Create a contract that adheres to the following specifications.
-
----
-
-## Contract
-
-Review the contract in the starter snippet called `ArraysExercise`. It contains an array called `numbers` that is initialized with the numbers 1–10. Copy and paste this into your file.
-
-```solidity
-contract ArraysExercise {
- uint[] public numbers = [1,2,3,4,5,6,7,8,9,10];
-}
-```
-
-Add the following functions:
-
-### Return a Complete Array
-
-The compiler automatically adds a getter for individual elements in the array, but it does not automatically provide functionality to retrieve the entire array.
-
-Write a function called `getNumbers` that returns the entire `numbers` array.
-
-### Reset Numbers
-
-Write a `public` function called `resetNumbers` that resets the `numbers` array to its initial value, holding the numbers from 1-10.
-
-
-We'll award the pin for any solution that works, but one that **doesn't** use `.push()` is more gas-efficient!
-
-
-
-
-Remember, _anyone_ can call a `public` function! You'll learn how to protect functionality in another lesson.
-
-
-
-### Append to an Existing Array
-
-Write a function called `appendToNumbers` that takes a `uint[] calldata` array called `_toAppend`, and adds that array to the `storage` array called `numbers`, already present in the starter.
-
-### Timestamp Saving
-
-At the contract level, add an `address` array called `senders` and a `uint` array called `timestamps`.
-
-Write a function called `saveTimestamp` that takes a `uint` called `_unixTimestamp` as an argument. When called, it should add the address of the caller to the end of `senders` and the `_unixTimestamp` to `timestamps`.
-
-
-You'll need to research on your own to discover the correct _Special Variables and Functions_ that can help you with this challenge!
-
-
-
-### Timestamp Filtering
-
-Write a function called `afterY2K` that takes no arguments. When called, it should return two arrays.
-
-The first should return all timestamps that are more recent than January 1, 2000, 12:00am. To save you a click, the Unix timestamp for this date and time is `946702800`.
-
-The second should return a list of `senders` addresses corresponding to those timestamps.
-
-### Resets
-
-Add `public` functions called `resetSenders` and `resetTimestamps` that reset those storage variables.
-
----
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/arrays/arrays-in-solidity-vid.mdx b/docs/learn/arrays/arrays-in-solidity-vid.mdx
deleted file mode 100644
index d16a816be..000000000
--- a/docs/learn/arrays/arrays-in-solidity-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Arrays
-description: Learn about the unique properties of arrays in Solidity.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/arrays/arrays-in-solidity.mdx b/docs/learn/arrays/arrays-in-solidity.mdx
deleted file mode 100644
index e342b7761..000000000
--- a/docs/learn/arrays/arrays-in-solidity.mdx
+++ /dev/null
@@ -1,142 +0,0 @@
----
-title: Arrays
-sidebarTitle: Arrays Guide
-description: An overview of how arrays work in Solidity.
-hide_table_of_contents: false
----
-
-Solidity arrays are collections of the same type, accessed via an index, the same as any other language. Unlike other languages, there are three types of arrays - _storage_, _memory_, and _calldata_. Each has their own properties and constraints.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Describe the difference between storage, memory, and calldata arrays
-
----
-
-## Storage, Memory, and Calldata
-
-The `storage`, `memory`, or `calldata` keywords are required when declaring a new [reference type] variable. This keyword determines the [data location] where the variable is stored and how long it will persist.
-
-### Storage
-
-The `storage` keyword is used to assign state variables that become a part of the blockchain as a part of the _storage_ for your contract. These remain as assigned until modified, for the lifetime of the contract.
-
-Storage is **very expensive** compared to most other environments. It costs a minimum of 20000 gas to [store a value] in a new storage slot, though it's cheaper to update that value after the initial assignment (~5000+ gas).
-
-This cost isn't a reason to be afraid of using storage. In the long run, writing clear, maintainable, and logical code will always cost less than jumping through hoops to save gas here and there. Just be as thoughtful with storage on the EVM as you would be with computation in most other environments.
-
-### Memory
-
-The `memory` keyword creates temporary variables that only exist within the scope in which they are created. Memory is less expensive than storage, although this is relative. There are often circumstances where it is cheaper to work directly in storage rather than convert to memory and back. Copying from one location to another can be quite expensive!
-
-### Calldata
-
-The `calldata` storage location is where function arguments are stored. It is non-modifiable and the Solidity docs recommend using it where possible to avoid unnecessary copying, because it can't be modified. You'll learn more about this later, but doing so can help prevent confusing bugs when calling a function from another contract that takes in values from that contract's `storage`.
-
----
-
-## Array Data Locations
-
-Arrays behave differently based on their data location. Assignment behavior also depends on data location. To [quote the docs]:
-
-> - Assignments between `storage` and `memory` (or from `calldata`) always create an independent copy.
->
-> - Assignments from `memory` to `memory` only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data.
->
-> - Assignments from `storage` to a **local** storage variable also only assign a reference.
->
-> - All other assignments to `storage` always copy. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference.
-
-### Storage Arrays
-
-_Arrays_ in `storage` are passed by _reference_. In other words, if you assign a _storage array_ half a dozen names, any changes you make will always modify the original, underlying storage array.
-
-```solidity
-contract StorageArray {
- // Variables declared at the class level are always `storage`
- uint[] arr = [1, 2, 3, 4, 5];
-
- function function_1() public {
- uint[] storage arr2 = arr;
-
- arr2[0] = 99; // <- arr is now [99, 2, 3, 4, 5];
- }
-}
-```
-
-You cannot use a `storage` array as a function parameter, and you cannot write a function that `return`s a `storage` array.
-
-Storage arrays are dynamic, unless they are declared with an explicit size. However, their functionality is limited compared to other languages. The `.push(value)` function works as expected. The `.pop()` function removes the last value of an array, but it **does not** return that value. You also **may not** use `.pop()` with an index to remove an element from the middle of an array, or to remove more than one element.
-
-You can use the `delete` keyword with an array. Doing so on an entire array will reset the array to zero length. Calling it on an element within the array will reset that value to its default. It **will not** resize the array!
-
-```solidity
-uint[] arr_2 = [1, 2, 3, 4, 5];
-function function_2(uint _num) public returns(uint[] memory) {
- arr_2.push(_num); // <- arr_2 is [1, 2, 3, 4, 5, <_num>]
-
- delete arr_2[2]; // <- arr_2 is [1, 2, 0, 4, 5, <_num>]
-
- arr_2.pop(); // <- arr_2 is [1, 2, 0, 4, 5] (_num IS NOT returned by .pop())
-
- delete arr_2; // <- arr_2 is []
-
- return arr_2; // <- returns []
-}
-```
-
-Storage arrays are implicitly convertible to `memory` arrays.
-
-### Memory Arrays
-
-Arrays declared as `memory` are temporary and only exist within the scope in which they are created. Arrays in `memory` are **not** dynamic and must be declared with a fixed size. This can be done at compile time, by declaring a size inside the `[]` or during runtime by using the `new` keyword. Finally, `memory` arrays can be implicitly cast from `storage` arrays.
-
-```solidity
-function function_3(uint _arrSize) public {
- uint[5] memory arrSizeFive;
- uint[] memory arrWithCustomSize = new uint[](_arrSize);
- uint[] memory localCopyOfArr = arr;
- // ...do something
-}
-```
-
-The declaration pattern impacts gas cost, though keep in mind that the first two examples are empty, and would cost additional gas depending on how they are eventually filled.
-
-```solidity
-function declareMemoryArrays() public view {
- uint[5] memory simpleArr; // this line costs 135 gas
- uint[] memory emptyArr = new uint[](5); // This line costs 194 gas
- uint[] memory arrCopy = arr; // This line costs 13166 gas
-}
-```
-
-The lack of dynamic `memory` arrays can require some gymnastics if you need to create an array where the size is not initially known. Depending on the specific needs of the problem, valid solutions for filtering an array and returning a smaller array could include:
-
-- Looping through a larger array twice, first to count the number, then to copy the appropriate elements
-- Tracking the number of elements that meet condition X with a storage variable, then instantiating the array with `[] memory filteredArray = new [](numX);`
-- Using multiple data structures to track references to different subsets
-
-### Calldata Arrays
-
-Arrays in `calldata` are read only. Otherwise, they function the same as any other array.
-
-Array [slices] are currently only implemented for `calldata` arrays.
-
----
-
-## Conclusion
-
-In this lesson, you've learned the differences between the `memory`, `storage`, and `calldata` data locations. You've also learned how they apply to arrays, with each having its own properties, restrictions, and costs.
-
----
-
-
-[data location]: https://docs.soliditylang.org/en/v0.8.17/types.html?highlight=calldata#data-location
-[reference type]: https://docs.soliditylang.org/en/v0.8.17/types.html?highlight=array#reference-types
-[store a value]: https://github.com/wolflo/evm-opcodes/blob/main/gas.md#a7-sstore
-[quote the docs]: https://docs.soliditylang.org/en/v0.8.17/types.html?#data-location-and-assignment-behaviour
-[slices]: https://docs.soliditylang.org/en/v0.8.17/types.html?#array-slices
diff --git a/docs/learn/arrays/filtering-an-array-sbs.mdx b/docs/learn/arrays/filtering-an-array-sbs.mdx
deleted file mode 100644
index 608f24782..000000000
--- a/docs/learn/arrays/filtering-an-array-sbs.mdx
+++ /dev/null
@@ -1,230 +0,0 @@
----
-title: Filtering an Array
-sidebarTitle: Filtering Arrays
-description: Explore techniques to filter an array.
-hide_table_of_contents: false
----
-
-In this exercise, you'll explore two different solutions for filtering an array in Solidity. By doing so, you'll gain a better understanding of the constraints present while working with arrays, and have the chance to learn and compare the gas costs of different approaches.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Write a function that can return a filtered subset of an array
-
----
-
-## First Pass Solution
-
-### Setup
-
-Create a new workspace in Remix and add a file called `ArrayDemo.sol` containing a `contract` called `ArrayDemo`. Initialize an array containing the numbers from 1 to 10. Add a stub for a function called `getEvenNumbers` that returns a `uint[] memory`.
-
-```solidity
-contract ArrayDemo {
- uint[] public numbers = [1,2,3,4,5,6,7,8,9,10];
-
- function getEvenNumbers() external view returns(uint[] memory) {
- // TODO
- }
-}
-```
-
-
-You don't have to declare the size of the memory array to be returned. You usually don't want to either, unless the results will always be the same, known size.
-
-
-
-### Finding the Number of Even Numbers
-
-We need to initialize a `memory` array to hold the results, but to do so, we need to know how big to make the array. Don't be tempted to count the number of evens in `numbers`, as what happens if we modify it later?
-
-The simple and obvious solution is to simply iterate through `numbers` and count how many even numbers are present. You could add that functionality in `getEvenNumbers()`, but it might be useful elsewhere, so a better practice would be to separate these concerns into another function.
-
-Go ahead and write it on your own. It needs to:
-
-- Instantiate a `uint` to hold the results
-- Iterate through all values in `numbers` and increment that number if the value is even
-- Return the result
-
-You should end up with something like:
-
-
-
-```solidity
-function _countEvenNumbers() internal view returns(uint) {
- uint result = 0;
-
- for(uint i = 0; i < numbers.length; i++) {
- if(numbers[i] % 2 == 0) {
- result++;
- }
- }
-
- return result;
-}
-```
-
-
-The `_` in front of the function name is a practice used by some developers, in Solidity and in other languages, to indicate visually that this function is intended for internal use only.
-
-### Returning Only Even Numbers
-
-Now that we have a method to find out how big the return array needs to be in `getEvenNumbers()`, we can simply loop through `numbers`, and add the even numbers to the array to be returned.
-
-Finish the function on your own. It needs to:
-
-- Determine the number of results and instantiate an array that size
-- Loop through the `numbers` array and if a given number is even, add it to the next unused index in the results array
-
-You should end up with something like:
-
-
-
-```solidity
-
-function getEvenNumbers() external view returns(uint[] memory) {
- uint resultsLength = _countEvenNumbers();
- uint[] memory results = new uint[](resultsLength);
- uint cursor = 0;
-
- for(uint i = 0; i < numbers.length; i++) {
- if(numbers[i] % 2 == 0) {
- results[cursor] = numbers[i];
- cursor++;
- }
- }
-
- return results;
-}
-
-```
-
-
-
-Did you catch the compiler warning about `view`? You aren't modifying state, so you should mark it as such.
-
-### Testing the Function
-
-Deploy your contract and test the function. You should get a return of `[2,4,6,8,10]`. The total gas cost will be about 63,947, depending on if you used the same helper variables, etc.
-
----
-
-## Optimizing the Function
-
-It does seem inefficient to loop through the same array twice. What if we instead kept track of how many even numbers to expect. That way, we would only need to loop once, thus saving gas! Right?
-
-Only one way to find out.
-
-### Tracking Relevant Data
-
-Add a contract-level variable called `numEven`, and initialize it with **5**, the number of even numbers in the array. Modify `getEvenNumbers()` to use `numEven` instead of the `_countEvenNumbers()` function. It should now look like:
-
-
-
-```solidity
-function getEvenNumbers() external view returns(uint[] memory) {
- uint resultsLength = numEven; // <- Changed here
- uint[] memory results = new uint[](resultsLength);
- uint cursor = 0;
-
- for(uint i = 0; i < numbers.length; i++) {
- if(numbers[i] % 2 == 0) {
- results[cursor] = numbers[i];
- cursor++;
- }
- }
-
- return results;
-}
-```
-
-
-
-Redeploy and test again. Success, the function now only costs about 57,484 gas to run! Except there is a catch. Remember, it's going to cost about 5000 gas to update `numEven` **each time** the array adds an even number.
-
-### A More Realistic Accounting
-
-As we considered above, in a real-world example, we wouldn't declare the array up front, it would be modified over time. A slightly more realistic example would be to fill the array with a function.
-
-Change the declaration for `numbers` and `numEven` so that they have their respective default values to begin with.
-
-```solidity
-uint[] public numbers;
-uint numEven;
-```
-
-Add a new function called `debugLoadArray` that takes a `uint` called `_number` as an argument, and fills the array by looping through `_number` times, pushing each number into the array. **For now, _don't_ update `numEven`**.
-
-
-
-```solidity
-function debugLoadArray(uint _number) external {
- for(uint i = 0; i < _number; i++) {
- numbers.push(i);
- }
-}
-```
-
-
-
-Test out the function by loading in **10** numbers. It costs about 249,610 gas to load the array. Now, add functionality to **also** increment `numEven` when the number added is even. We can't just calculate it, because although the numbers are sequential in the debug function, they might not be in real world use.
-
-
-
-```solidity
-function debugLoadArray(uint _number) external {
- for(uint i = 0; i < _number; i++) {
- numbers.push(i);
- if(i % 2 == 0) {
- numEven++;
- }
- }
-}
-```
-
-
-
-**Be sure to redeploy** and try again with **10** numbers. This time, the cost was about 275,335 gas. That's almost 26,000 more gas in an effort to save the 5,000 gas needed to run `_countEvenNumbers()`.
-
-### Looking at the Big Picture
-
-What about more? What if there are a thousand numbers in the array? What about a million?
-
-Let's start with 500, any more will break the Remix EVM simulation, and/or would trigger an out of gas error because we're approaching the gas limit for the entire block.
-
-**Comment out** the `if` statement in `debugLoadArray` that checks for even numbers and load 500 numbers. The Remix EVM should be able to handle this, but it might hang up for a moment, or even crash. (You can also do this experiment with 250 numbers instead.)
-
-```solidity
-function debugLoadArray(uint _number) external {
- for(uint i = 0; i < _number; i++) {
- numbers.push(i);
- // if(i % 2 == 0) {
- // numEven++;
- //}
- }
-}
-```
-
-You'll get a result of about 11,323,132 gas to load the array. That's a lot! The target total gas for a single block is 15 million, and the limit is 30 million.
-
-Try again with the code to increment `numEven`. You should get about 11,536,282, or an increase of about 213,150 gas.
-
-Now, test out `getEvenNumbers()` using `numEven` vs. using `_countEvenNumbers()`. With `numEven`, it should cost about 1,578,741 gas to find the even numbers. Using `_countEvenNumbers()`, that cost increases to 1,995,579 gas, an increase of 416,838 gas.
-
-### Which is Better?
-
-As is often the case with code, it depends. You might think that the experiment makes things obvious. Paying 213k gas up front to track `_numEven` results in a savings of over 400k gas when filtering for even numbers. Even better, you might realize that the upfront cost difference will be spread across all of your users over time, making them almost trivial. You also might think that it's possible that the filter function could be called dozens of times for each time 500 numbers are loaded.
-
-These are all valid considerations that you should evaluate as you are developing your code solution to a business problem. One last critical element to consider is that there is only a gas cost to read from the blockchain if it's another contract calling the function. It **doesn't** cost any gas to call `view` or `pure` functions from a front end or app.
-
-If `getEvenNumbers` will never be called by another contract, then using `numEven` might cost more for no benefit!
-
----
-
-## Conclusion
-
-In this lesson, you've explored a few different approaches to a problem. You've learned how to filter an array, but more importantly, you've learned some of the specific considerations in blockchain development. Finally, you've seen that pushing 500 integers to an array, usually a trivial operation, is very large and very expensive on the EVM.
diff --git a/docs/learn/arrays/fixed-size-arrays-vid.mdx b/docs/learn/arrays/fixed-size-arrays-vid.mdx
deleted file mode 100644
index 4d3189d75..000000000
--- a/docs/learn/arrays/fixed-size-arrays-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Fixed-Size Arrays
-sidebarTitle: Fixed Size Arrays
-description: Learn about fixed-size arrays.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/arrays/writing-arrays-in-solidity-vid.mdx b/docs/learn/arrays/writing-arrays-in-solidity-vid.mdx
deleted file mode 100644
index a4128612e..000000000
--- a/docs/learn/arrays/writing-arrays-in-solidity-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Writing Arrays in Solidity
-sidebarTitle: Writing Arrays
-description: Learn how to write arrays in Solidity.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/contracts-and-basic-functions/basic-functions-exercise.mdx b/docs/learn/contracts-and-basic-functions/basic-functions-exercise.mdx
deleted file mode 100644
index 2aa5e4be6..000000000
--- a/docs/learn/contracts-and-basic-functions/basic-functions-exercise.mdx
+++ /dev/null
@@ -1,38 +0,0 @@
----
-title: 'Basic Functions Exercise'
-description: Exercise - Create and deploy a contract with simple math functions.
-hide_table_of_contents: false
----
-
-Each module in this course will contain exercises in which you are given a specification for a contract **without** being given specific instructions on how to build the contract. You must use what you've learned to figure out the best solution on your own!
-
-
-Once you've learned how to deploy your contracts to a test network, you'll be given the opportunity to submit your contract address for review by an onchain unit test. If it passes, you'll receive an NFT pin recognizing your accomplishment.
-
-**You'll deploy and submit this contract in the next module.**
-
-
-
-The following exercise asks you to create a contract that adheres to the following stated specifications.
-
-## Contract
-
-Create a contract called `BasicMath`. It should not inherit from any other contracts and does not need a constructor. It should have the following two functions:
-
-### Adder
-
-A function called `adder`. It must:
-
-- Accept two `uint` arguments, called `_a` and `_b`
-- Return a `uint` `sum` and a `bool` `error`
-- If `_a` + `_b` does not overflow, it should return the `sum` and an `error` of `false`
-- If `_a` + `_b` overflows, it should return `0` as the `sum`, and an `error` of `true`
-
-### Subtractor
-
-A function called `subtractor`. It must:
-
-- Accept two `uint` arguments, called `_a` and `_b`
-- Return a `uint` `difference` and a `bool` `error`
-- If `_a` - `_b` does not underflow, it should return the `difference` and an `error` of `false`
-- If `_a` - `_b` underflows, it should return `0` as the `difference`, and an `error` of `true`
diff --git a/docs/learn/contracts-and-basic-functions/basic-types.mdx b/docs/learn/contracts-and-basic-functions/basic-types.mdx
deleted file mode 100644
index 9c395733a..000000000
--- a/docs/learn/contracts-and-basic-functions/basic-types.mdx
+++ /dev/null
@@ -1,221 +0,0 @@
----
-title: Basic Types
-description: Introduction to basic types in Solidity.
-hide_table_of_contents: false
----
-
-import { Danger } from "/snippets/danger.mdx";
-
-Solidity contains most of the basic [types] you are used to from other languages, but their properties and usage are often a little different than other languages and are likely much more restrictive. In particular, Solidity is a very **explicit** language and will not allow you to make inferences most of the time.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Categorize basic data types
-- List the major differences between data types in Solidity as compared to other languages
-- Compare and contrast signed and unsigned integers
-
----
-
-## Common Properties
-
-In Solidity, [types] must always have a value and are never `undefined`, `null`, or `none`. Because of this, each type has a default value. If you declare a variable without assigning a value, it will instead have the default value for that type. This property can lead to some tricky bugs until you get used to it.
-
-```solidity
-uint defaultValue;
-uint explicitValue = 0;
-
-// (defaultValue == explicitValue) <-- true
-```
-
-Types can be cast from one type to another, but not as freely as you may expect. For example, to convert a `uint256` into a `int8`, you need to cast twice:
-
-```solidity
-uint256 first = 1;
-int8 second = int8(int256(first));
-```
-
-
-Overflow/underflow protection (described below), does not provide protection when casting.
-
-```solidity
-uint256 first = 256;
-int8 second = int8(int256(first)); // <- The value stored in second is 0
-```
-
-
-
-## Boolean
-
-[Booleans] can have a value of `true` or `false`. Solidity does not have the concept of _truthy_ or _falsey_, and non-boolean values cannot be cast to bools by design. The short conversation in this [issue] explains why, and explains the philosophy why.
-
-### Logical Operators
-
-Standard logical operators (`!`, `&&`, `||`, `==`, `!=`) apply to booleans. Short-circuiting rules do apply, which can sometimes be used for gas savings since if the first operator in an `&&` is `false` or `||` is `true`, the second will not be evaluated. For example, the following code will execute without an error, despite the divide by zero in the second statement.
-
-```solidity
-// Bad code for example. Do not use.
-uint divisor = 0;
-if(1 < 2 || 1 / divisor > 0) {
- // Do something...
-}
-```
-
-You cannot use any variant of `>` or `<` with booleans, because they cannot be implicitly or explicitly cast to a type that uses those operators.
-
----
-
-## Numbers
-
-Solidity has a number of types for signed and unsigned [integers], which are not ignored as much as they are in other languages, due to potential gas-savings when storing smaller numbers. Support for [fixed point numbers] is under development, but is not fully implemented as of version `0.8.17`.
-
-Floating point numbers are not supported and are not likely to be. Floating precision includes an inherent element of ambiguity that doesn't work for explicit environments like blockchains.
-
-### Min, Max, and Overflow
-
-Minimum and maximum values for each type can be accessed with `type().min` and `type().max`. For example, `type(uint).min` is **0**, and `type(uint).max` is equal to **2^256-1**.
-
-An overflow or underflow will cause a transaction to _revert_, unless it occurs in a code block that is marked as [unchecked].
-
-### `uint` vs. `int`
-
-In Solidity, it is common practice to favor `uint` over `int` when it is known that a value will never (or should never) be below zero. This practice helps you write more secure code by requiring you to declare whether or not a given value should be allowed to be negative. Use `uint` for values that should not, such as array indexes, account balances, etc. and `int` for a value that does **need** to be negative.
-
-### Integer Variants
-
-Smaller and larger variants of integers exist in many languages but have fallen out of favor in many instances, in part because memory and storage are relatively cheap. Solidity supports sizes in steps of eight from `uint8` to `uint256`, and the same for `int`.
-
-Smaller sized integers are used to optimize gas usage in storage operations, but there is a cost. The EVM operates with 256 bit words, so operations involving smaller data types must be cast first, which costs gas.
-
-`uint` is an alias for `uint256` and can be considered the default.
-
-### Operators
-
-Comparisons (`<=`, `<`, `==`, `!=`, `>=`, `>`) and arithmetic (`+`, `-`, `*`, `/`, `%`, `**`) operators are present and work as expected. You can also use bit and shift operators.
-
-`uint` and `int` variants can be compared directly, such as `uint8` and `uint256`, but you must cast one value to compare a `uint` to an `int`.
-
-```solidity
-uint first = 1;
-int8 second = 1;
-
-if(first == uint8(second)) {
- // Do something...
-}
-```
-
----
-
-## Addresses
-
-The [address] type is a relatively unique type representing a wallet or contract address. It holds a 20-byte value, similar to the one we explored when you deployed your _Hello World_ contract in _Remix_. `address payable` is a variant of `address` that allows you to use the `transfer` and `send` methods. This distinction helps prevent sending Ether, or other tokens, to a contract that is not designed to receive it. If that were to happen, the Ether would be lost.
-
-Addresses are **not** strings and do not need quotes when represented literally, but conversions from `bytes20` and `uint160` are allowed.
-
-```solidity
-address existingWallet = 0xd9145CCE52D386f254917e481eB44e9943F39138;
-```
-
-### Members of Addresses
-
-Addresses contain a number of functions. `balance` returns the balance of an address, and `transfer`, mentioned above, can be used to send `ether`.
-
-```solidity
-function getBalance(address _address) public view returns(uint) {
- return _address.balance;
-}
-```
-
-Later on, you'll learn about `call`, `delegatecall`, and `staticcall`, which can be used to call functions deployed in other contracts.
-
----
-
-## Contracts
-
-When you declare a [contract], you are defining a type. This type can be used to instantiate one contract as a local variable inside a second contract, allowing the second to interact with the first.
-
----
-
-## Byte Arrays and Strings
-
-[Byte arrays] come as both fixed-size and dynamically-sized. They hold a sequence of bytes. Arrays are a little more complicated than in other languages and will be covered in-depth later.
-
-### Strings
-
-Strings are arrays in Solidity, not a type. You cannot concat them with `+`, but as of _0.8.12_, you can use `string.concat(first, second)`. They are limited to printable characters and escaped characters. Casting other data types to `string` is at best tricky, and sometimes impossible.
-
-Generally speaking, you should be deliberate when working with strings inside of a smart contract. Don't be afraid to use them when appropriate, but if possible, craft and display messages on the front end rather than spending gas to assemble them on the back end.
-
----
-
-## Enums
-
-[Enums] allow you to apply human-readable labels to a list of unsigned integers.
-
-```solidity
-enum Flavors { Vanilla, Chocolate, Strawberry, Coffee }
-
-Flavors chosenFlavor = Flavors.Coffee;
-```
-
-Enums can be explicitly cast to and from `uint`, but not implicitly. They are limited to 256 members.
-
----
-
-## Constant and Immutable
-
-The [constant and immutable] keywords allow you to declare variables that cannot be changed. Both result in gas savings because the compiler does not need to reserve a storage slot for these values.
-
-As of _0.8.17_, `constant` and `immutable` are not fully implemented. Both are supported on [value types], and `constant` can also be used with strings.
-
-### Constant
-
-Constants can be declared at the file level, or at the contract level. In Solidity, modifiers come after the type declaration. You must initialize a value when declaring a constant. Convention is to use SCREAMING_SNAKE_CASE for constants.
-
-```solidity
-uint constant NUMBER_OF_TEAMS = 10;
-
-contract Cars {
- uint constant NUMBER_OF_CARS = 20;
-}
-```
-
-At compilation, the compiler replaces every instance of the constant variable with its literal value.
-
-### Immutable
-
-The immutable keyword is used to declare variables that are set once within the constructor, which are then never changed:
-
-```solidity
-contract Season {
- immutable numberOfRaces;
-
- constructor(uint _numberOfRaces) {
- numberOfRaces = _numberOfRaces;
- }
-}
-```
-
----
-
-## Conclusion
-
-You've learned the usage and some of the unique quirks of common variable types in Solidity. You've seen how overflow and underflow are handled and how that behavior can be overridden. You've learned why unsigned integers are used more commonly than in other languages, why floats are not present, and have been introduced to some of the quirks of working with strings. Finally, you've been introduced to the address and contract data types.
-
----
-
-[types]: https://docs.soliditylang.org/en/v0.8.17/types.html
-[Booleans]: https://docs.soliditylang.org/en/v0.8.17/types.html#booleans
-[issue]: https://github.com/ethereum/solidity/issues/1200
-[integers]: https://docs.soliditylang.org/en/v0.8.17/types.html#integers
-[fixed point numbers]: https://docs.soliditylang.org/en/v0.8.17/types.html#fixed-point-numbers
-[unchecked]: https://docs.soliditylang.org/en/v0.8.17/control-structures.html#unchecked
-[address]: https://docs.soliditylang.org/en/v0.8.17/types.html#address
-[contract]: https://docs.soliditylang.org/en/v0.8.17/types.html#contract-types
-[Byte arrays]: https://docs.soliditylang.org/en/v0.8.17/types.html#fixed-size-byte-arrays
-[Enums]: https://docs.soliditylang.org/en/v0.8.17/types.html#enums
-[constant and immutable]: https://docs.soliditylang.org/en/v0.8.17/contracts.html?constant-and-immutable-state-variables#constant-and-immutable-state-variables
-[value types]: https://docs.soliditylang.org/en/v0.8.17/types.html#value-types
diff --git a/docs/learn/contracts-and-basic-functions/hello-world-step-by-step.mdx b/docs/learn/contracts-and-basic-functions/hello-world-step-by-step.mdx
deleted file mode 100644
index 69242090c..000000000
--- a/docs/learn/contracts-and-basic-functions/hello-world-step-by-step.mdx
+++ /dev/null
@@ -1,205 +0,0 @@
----
-sidebarTitle: Hello World Guide
-title: Hello World
-description: Write your first contract with Solidity.
-hide_table_of_contents: false
----
-
-As is tradition, we'll begin coding with a variant of "Hello World" written as a smart contract. There isn't really a console to write to\*, so instead, we'll write a contract that says hello to the sender, using the name they provide.
-
-\*You will be able to use `console.log` with _Hardhat_, with some restrictions.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Construct a simple "Hello World" contract
-- List the major differences between data types in Solidity as compared to other languages
-- Select the appropriate visibility for a function
-
----
-
-## Hello World
-
-Writing "Hello World" in a smart contract requires a little more consideration than in other languages. Your code is deployed remotely, but it isn't running on a server where you can access logs, or on your local machine where you have access to a console. One way to do it is to write a function that returns "Hello World".
-
-### Creating the Contract
-
-To create a contract:
-
-1. Create a new workspace in Remix.
-2. Name it `Hello World` and delete the `.deps` folder.
-3. Leave `.prettierrc.json` and click the settings gear in the bottom left.
-4. Uncheck the top option to _Generate contract metadata..._
-5. Open the _Solidity Compiler_ plugin and enable _Auto compile_.
-6. Create a new folder called `contracts`, and within that folder, create a file called `hello-world.sol`.
-
-Solidity files usually start with a comment containing an [_SPDX-License-Identifier_]. It's not a requirement, but there are a couple of advantages to doing this. First, everything you deploy on the blockchain is public. This doesn't mean you are giving away everything you deploy for free, nor does it mean you have the right to use the code from any deployed contract. The license determines allowed usage and is generally protected by international copyright laws, the same as any other code.
-
-If you don't want to give a license, you can put `UNLICENSED`. Common open source licenses, such as `MIT` and `GPL-3.0` are popular as well. Add your license identifier:
-
-```Solidity
-// SPDX-License-Identifier: MIT
-```
-
-Below the license identifier, you need to specify which [version] of Solidity the compiler should use to compile your code. If by the time you read this, the version has advanced, you should try to use the most current version. Doing so may cause you to run into unexpected errors, but it's great practice for working in real-world conditions!
-
-```Solidity
-pragma solidity 0.8.17;
-```
-
-Finally, add a `contract` called `HelloWorld`. You should end up with:
-
-```Solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity 0.8.17;
-
-contract HelloWorld {
-
-}
-```
-
-### SayHello Function
-
-Add a function to your contract called `SayHello`:
-
-```Solidity
-function SayHello() {
-
-}
-```
-
-You'll get a compiler syntax error for _No visibility specified. Did you intend to add "public"?_.
-
-Is `public` the most appropriate [visibility specifier]?
-
-It would work, but you won't be calling this function from within the contract, so `external` is more appropriate.
-
-You also need to specify a return type, and we've decided this function should return a string. You'll learn more about this later, but in Solidity, many of the more complex types require you to specify if they are `storage` or `memory`. You can then have your function return a string of `"Hello World!"`.
-
-Don't forget your semicolon. They're mandatory in Solidity!
-
-You should have:
-
-```Solidity
-function SayHello() external returns (string memory) {
- return "Hello World!";
-}
-```
-
-Before you deploy, check the `Compiler` plugin. You've got one last warning:
-
-> Warning: Function state mutability can be restricted to pure
-
-[Modifiers] are used to modify the behavior of a function. The `pure` modifier prevents the function from modifying, or even accessing state. While not mandatory, using these modifiers can help you and other programmers know the intention and impact of the functions you write. Your final function should be similar to:
-
-```Solidity
-function SayHello() external pure returns (string memory) {
- return "Hello World!";
-}
-```
-
-### Deployment and Testing
-
-Confirm that there is a green checkmark on the icon for the compiler plugin, and then switch to the _Deploy & Run Transactions_ plugin.
-
-Click the _Deploy_ button and your contract should appear in _Deployed Contracts_. Open it up and then click the _SayHello_ button. Did it work?
-
-You should see your message below the button. Another option to see the return for your `HelloWorld` function is to expand the entry in the console. You should see a _decoded output_ of:
-
-```text
-{
- "0": "string: Hello World!"
-}
-```
-
----
-
-## Greeter
-
-Now, let's modify your say hello function to greet a user by name, instead of just saying "Hello World!"
-
-### First Pass Attempt
-
-You'd probably expect this to be pretty easy. Start by changing the name of the function (or adding a new one) to `Greeter` and giving it a parameter for a `string memory _name`. The underscore is a common convention to mark functions and variables as internal to their scope. Doing so helps you tell the difference between a storage variable, and a memory variable that only exists within the call.
-
-Finally, try creating a return string similar to how you might in another language with `"Hello " + _name`. You should have:
-
-```Solidity
-// Bad code example: Does not work
-function Greeter(string memory _name) external pure returns (string memory) {
- return "Hello " + _name + "!";
-}
-```
-
-Unfortunately, this does not work in Solidity. The error message you receive is a little confusing:
-
-> TypeError: Operator + not compatible with types literal_string "Hello" and string memory.
-
-You might think that there is some sort of type casting or conversion error that could be solved by explicitly casting the string literal to string memory, or vice versa. This is a great instinct. Solidity is a very explicit language.
-
-However, you receive a similar error with `"Hello " + "world"`.
-
-String concatenation is possible in Solidity, but it's a bit more complicated than most languages, for good reason. Working with string costs a large amount of gas, so it's usually better to handle this sort of processing on the front end.
-
-### Plan B
-
-We still want to return something with the name provided by the user, so let's try something a little different. Solidity is a _variadic_ language, which means it allows functions to return more than one value.
-
-Modify your return declaration: `returns (string memory, string memory)`
-
-Now, your function can return a [tuple] containing two strings!
-
-`return ("Hello", _name)`;
-
-Deploy and test your contract. You should get a _decoded output_ with:
-
-```text
-{
- "string _name": "Your Name"
-}
-```
-
-### Full Example Code
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity 0.8.17;
-
-contract HelloWorld {
-
- function SayHello() external pure returns (string memory) {
- return "Hello World!";
- }
-
- // Bad code example: Does not work
- // function Greeter(string memory _name) external pure returns (string memory) {
- // return "Hello " + _name;
- // }
-
- function Greeter(string memory _name) external pure returns (string memory, string memory) {
- return ("Hello", _name);
- }
-}
-```
-
----
-
-## Conclusion
-
-Congratulations! You've written and tested your first smart contract! You selected a license and a version of Solidity. You declared a contract and added a function that returns a value.
-
-You also learned more about some of the ways in which Solidity is more challenging to work with than other languages, and the additional elements you sometimes need to declare functions and types.
-
----
-
-
-[_SPDX-License-Identifier_]: https://spdx.org/licenses/
-[version]: https://docs.soliditylang.org/en/v0.8.17/layout-of-source-files.html?#version-pragma
-[visibility specifier]: https://docs.soliditylang.org/en/v0.8.17/cheatsheet.html?#function-visibility-specifiers
-[modifiers]: https://docs.soliditylang.org/en/v0.8.17/cheatsheet.html?#modifiers
-[tuple]: https://en.wikipedia.org/wiki/Tuple
diff --git a/docs/learn/contracts-and-basic-functions/intro-to-contracts-vid.mdx b/docs/learn/contracts-and-basic-functions/intro-to-contracts-vid.mdx
deleted file mode 100644
index beb52d488..000000000
--- a/docs/learn/contracts-and-basic-functions/intro-to-contracts-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Introduction to Contracts
-description: Learn about the core structure of EVM programs.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/control-structures/control-structures-exercise.mdx b/docs/learn/control-structures/control-structures-exercise.mdx
deleted file mode 100644
index e360c9af4..000000000
--- a/docs/learn/control-structures/control-structures-exercise.mdx
+++ /dev/null
@@ -1,57 +0,0 @@
----
-title: Control Structures Exercise
-sidebarTitle: Exercise
-description: Exercise - Demonstrate your knowledge of control structures.
-hide_table_of_contents: false
----
-
-Create a contract that adheres to the following specifications:
-
----
-
-## Contract
-
-Create a single contract called `ControlStructures`. It should not inherit from any other contracts and does not need a constructor. It should have the following functions:
-
-### Smart Contract FizzBuzz
-
-Create a function called `fizzBuzz` that accepts a `uint` called `_number` and returns a `string memory`. The function should return:
-
-- "Fizz" if the `_number` is divisible by 3
-- "Buzz" if the `_number` is divisible by 5
-- "FizzBuzz" if the `_number` is divisible by 3 and 5
-- "Splat" if none of the above conditions are true
-
-### Do Not Disturb
-
-Create a function called `doNotDisturb` that accepts a `uint` called `_time`, and returns a `string memory`. It should adhere to the following properties:
-
-- If `_time` is greater than or equal to 2400, trigger a `panic`
-- If `_time` is greater than 2200 or less than 800, `revert` with a custom error of `AfterHours`, and include the time provided
-- If `_time` is between `1200` and `1259`, `revert` with a string message "At lunch!"
-- If `_time` is between 800 and 1199, return "Morning!"
-- If `_time` is between 1300 and 1799, return "Afternoon!"
-- If `_time` is between 1800 and 2200, return "Evening!"
-
----
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/control-structures/control-structures.mdx b/docs/learn/control-structures/control-structures.mdx
deleted file mode 100644
index 605c12c6b..000000000
--- a/docs/learn/control-structures/control-structures.mdx
+++ /dev/null
@@ -1,233 +0,0 @@
----
-title: Control Structures
-sidebarTitle: Overview
-description: Learn how to control code flow in Solidity.
-hide_table_of_contents: false
----
-
-
-Solidity supports many familiar control structures, but these come with additional restrictions and considerations due to the cost of gas and the necessity of setting a maximum amount of gas that can be spent in a given transaction.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Control code flow with `if`, `else`, `while`, and `for`
-- List the unique constraints for control flow in Solidity
-- Utilize `require` to write a function that can only be used when a variable is set to `true`
-- Write a `revert` statement to abort execution of a function in a specific state
-- Utilize `error` to control flow more efficiently than with `require`
-
----
-
-## Control Structures
-
-Solidity supports the basic conditional and iterative [control structures] found in other curly bracket languages, but it **does not** support more advanced statements such as `switch`, `forEach`, `in`, `of`, etc.
-
-Solidity does support `try`/`catch`, but only for calls to other contracts.
-
-
-[Yul] is an intermediate-level language that can be embedded in Solidity contracts and is documented within the docs for Solidity. Yul **does** contain the `switch` statement, which can confuse search results.
-
-
-
-### Conditional Control Structure Examples
-
-The `if`, `else if`, and `else`, statements work as expected. Curly brackets may be omitted for single-line bodies, but we recommend avoiding this as it is less explicit.
-
-```solidity
-function ConditionalExample(uint _number) external pure returns (string memory) {
- if(_number == 0) {
- return "The number is zero.";
- } else if(_number % 2 == 0) {
- return "The number is even and greater than zero.";
- } else {
- return "The number is odd and is greater than zero.";
- }
-}
-```
-
-### Iterative Control Structures
-
-The `while`, `for`, and `do`, keywords function the same as in other languages. You can use `continue` to skip the rest of a loop and start the next iteration. `break` will terminate execution of the loop, and you can use `return` to exit the function and return a value at any point.
-
-
-You can use `console.log` by importing `import "hardhat/console.sol";`. Doing so will require you to mark otherwise `pure` contracts as `view`.
-
-
-
-```solidity
-uint times; // Default value is 0!
-for(uint i = 0; i <= times; i++) {
- console.log(i);
-}
-
-uint timesWithContinue;
-for(uint i = 0; i <= timesWithContinue; i++) {
- if(i % 2 == 1) {
- continue;
- }
- console.log(i);
-}
-
-uint timesWithBreak;
-for(uint i = 0; i <= timesWithBreak; i++) {
- // Always stop at 7
- if(i == 7) {
- break;
- }
- console.log(i);
-}
-
-uint stopAt = 10;
-while(stopAt <= 10) {
- console.log(i);
- stopAt++;
-}
-
-uint doFor = 10;
-do {
- console.log(i);
- doFor++;
-} while(doFor <= 10);
-```
-
----
-
-## Error Handling
-
-Solidity contains a set of relatively unique, built-in functions and keywords to handle [errors]. They ensure certain requirements are met, and completely abort all execution of the function and revert any state changes that occurred during function execution. You can use these functions to help protect the security of your contracts and limit their execution.
-
-The approach may seem different than in other environments. If an error occurs partly through a high-stakes transaction such as transferring millions of dollars of tokens, you **do not** want execution to carry on, partially complete, or swallow any errors.
-
-### Revert and Error
-
-The `revert` keyword halts and reverses execution. It must be paired with a custom `error`. Revert should be used to prevent operations that are logically valid, but should not be allowed for business reasons. It is **not** a bug if a `revert` is triggered. Examples where `revert` and `error` would be used to control operations include:
-
-- Allowing only certain senders to access functionality
-- Preventing the withdrawal of a deposit before a certain date
-- Allowing inputs under certain state conditions and denying them under others
-
-Custom `error`s can be declared without parameters, but they are much more useful if you include them:
-
-```solidity
-error OddNumberSubmitted(uint _first, uint _second);
-function onlyAddEvenNumbers(uint _first, uint _second) public pure returns (uint) {
- if(_first % 2 != 0 || _second % 2 != 0) {
- revert OddNumberSubmitted(_first, _second);
- }
- return _first + _second;
-}
-```
-
-When triggered, the `error` provides the values in the parameters provided. This information is very useful when debugging, and/or to transmit information to the front end to share what has happened with the user:
-
-```text
-call to HelloWorld.onlyAddEvenNumbers errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-Error provided by the contract:
-OddNumberSubmitted
-Parameters:
-{
- "_first": {
- "value": "1"
- },
- "_second": {
- "value": "2"
- }
-}
-Debug the transaction to get more information.
-```
-
-You'll also encounter `revert` used as a function, returning a string error. This legacy pattern has been retained to maintain compatibility with older contracts:
-
-```solidity
-function oldRevertAddEvenNumbers(uint _first, uint _second) public pure returns (uint) {
- if(_first % 2 != 0 || _second % 2 != 0) {
- // Legacy use of revert, do not use
- revert("One of the numbers is odd");
- }
- return _first + _second;
-}
-```
-
-The error provided is less helpful:
-
-```text
-call to HelloWorld.oldRevertAddEvenNumbers errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-The reason provided by the contract: "One of the numbers is odd".
-Debug the transaction to get more information.
-```
-
-### Require
-
-The `require` function is falling out of favor because it uses more gas than the pattern above. You should still become familiar with it because it is present in innumerable contracts, tutorials, and examples.
-
-`require` takes a logical condition and a string error as arguments. It is more gas efficient to separate logical statements if they are not interdependent. In other words, don't use `&&` or `||` in a `require` if you can avoid it.
-
-For example:
-
-```solidity
-function requireAddEvenNumbers(uint _first, uint _second) public pure returns (uint) {
- // Legacy pattern, do not use
- require(_first % 2 == 0, "First number is not even");
- require(_second % 2 == 0, "Second number is not even");
-
- return _first + _second;
-}
-```
-
-The output error message will be the first one that fails. If you were to submit `1`, and `3` to this function, the error will only contain the first message:
-
-```test
-call to HelloWorld.requireAddEvenNumbers errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-The reason provided by the contract: "First number is not even".
-Debug the transaction to get more information.
-```
-
-### Assert and Panic
-
-The `assert` keyword throws a `panic` error if triggered. A `panic` is the same type of error that is thrown if you try to divide by zero or access an array out-of-bounds. It is used for testing internal errors and should never be triggered by normal operations, even with flawed input. You have a bug that should be resolved if an assert throws an exception:
-
-```solidity
-function ProcessEvenNumber(uint _validatedInput) public pure {
- // If assert triggers, input validation has failed. This should never
- // happen!
- assert(_validatedInput % 2 == 0);
- // Do something...
-}
-```
-
-The output here isn't as helpful, so you may wish to use one of the patterns above instead.
-
-```text
-call to HelloWorld.ProcessEvenNumber errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-Note: The called function should be payable if you send value and the value you send should be less than your current balance.
-Debug the transaction to get more information.
-```
-
----
-
-## Conclusion
-
-In this lesson, you've learned how to control code flow with standard conditional and iterative operators. You've also learned about the unique keywords Solidity uses to generate errors and reset changes if one of them has been triggered. You've been exposed to both newer and legacy methods of writing errors, and learned the difference between `assert` and `require`.
-
-
-
-[switch]: https://docs.soliditylang.org/en/v0.8.17/yul.html?#switch
-[yul]: https://docs.soliditylang.org/en/v0.8.17/yul.html
-[control structures]: https://docs.soliditylang.org/en/v0.8.17/control-structures.html
-[errors]: https://docs.soliditylang.org/en/v0.8.17/control-structures.html#error-handling-assert-require-revert-and-exceptions
diff --git a/docs/learn/control-structures/loops-vid.mdx b/docs/learn/control-structures/loops-vid.mdx
deleted file mode 100644
index f94184009..000000000
--- a/docs/learn/control-structures/loops-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Loops
-description: Explore loops in Solidity.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/control-structures/require-revert-error-vid.mdx b/docs/learn/control-structures/require-revert-error-vid.mdx
deleted file mode 100644
index 0ccf48745..000000000
--- a/docs/learn/control-structures/require-revert-error-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Require, Revert, and Error
-description: Handle errors in Solidity.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/control-structures/standard-control-structures-vid.mdx b/docs/learn/control-structures/standard-control-structures-vid.mdx
deleted file mode 100644
index 1b0edea6e..000000000
--- a/docs/learn/control-structures/standard-control-structures-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: If, Else, and Else If
-sidebarTitle: Standard Control Structures
-description: Learn how to control your code.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/deployment-to-testnet/contract-verification-sbs.mdx b/docs/learn/deployment-to-testnet/contract-verification-sbs.mdx
deleted file mode 100644
index 396f644aa..000000000
--- a/docs/learn/deployment-to-testnet/contract-verification-sbs.mdx
+++ /dev/null
@@ -1,68 +0,0 @@
----
-title: Contract Verification
-description: Verify your contract and interact with it.
-hide_table_of_contents: false
----
-
-Once your contract is deployed, you can verify it using a number of popular services. Doing so will let your users have confidence that your contract does what you claim, and will allow you to interact with it using a similar interface to what you used in Remix.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Verify a contract on the Base Sepolia testnet and interact with it in [BaseScan](https://sepolia.basescan.org/).
-
----
-
-### Verify the Contract
-
-Make sure you still have the address of the contract you deployed in the last article copied to the clipboard.
-
-You can interact with your deployed contract using Remix, the same as before, but it's also possible to interact with it through BaseScan. Paste your address in the search field to find it.
-
-On this page, you can review the balance, information about, and all the transactions that have ever occurred with your contract.
-
-Click the _Contract_ tab in the main panel. At the top is a message asking you to _Verify and Publish_ your contract source code.
-
-
-
-
-
-Verifying your contract maps the names of your functions and variables to the compiled byte code, which makes it possible to interact with the contract using a human-readable interface.
-
-Click the link. Your contract address is already entered.
-
-Under _Please select Compiler Type_ choose Solidity (Single file)
-
-For _Please Select Compiler Version_ select the version matching the `pragma` at the top of your source file. Our examples are currently using _v0.8.17+commit.8df45f5f_.
-
-For _Please select Open Source License Type_ pick the license that matches what you selected for your contract as the `SPDX-License-Identifier`. Pick _None_ if you followed the Solidity-recommended practice of using `UNLICENSED`.
-
-On the next page, copy and paste your source code in the window. Verify that you are not a robot, and click _Verify and Publish_. You should see a success message.
-
-
-
-
-
-Click the linked address to your contract to return to the contract page. You'll now see your code!
-
-
-If you have imports, you'll need to right-click on the name of the file and choose `Flatten`. Submit the newly generated `filename_flattened.sol` for verification.
-
-
-
-### Interact with the Contract
-
-You can now interact with your contract using BaseScan. Click the _Read Contract_ button. Both of your functions will be listed here and can be tested using the web interface.
-
-You won't have anything under _Write Contract_ because this contract doesn't have any functions that save data to state.
-
----
-
-## Conclusion
-
-With your contracts verified, you can interact with them using online tools and your users can be secure that your code does what you claim.
-
----
diff --git a/docs/learn/deployment-to-testnet/deployment-to-base-sepolia-sbs.mdx b/docs/learn/deployment-to-testnet/deployment-to-base-sepolia-sbs.mdx
deleted file mode 100644
index bb8d330b3..000000000
--- a/docs/learn/deployment-to-testnet/deployment-to-base-sepolia-sbs.mdx
+++ /dev/null
@@ -1,108 +0,0 @@
----
-sidebarTitle: Deploy to Base Sepolia
-title: Deployment to Base Sepolia
-description: Deploy your smart contract to a test network.
-hide_table_of_contents: false
----
-
-import { Danger } from "/snippets/danger.mdx";
-
-Remix contains a simulation of a blockchain that you can use to rapidly deploy and test your contracts. This simulation only exists within your browser so you can't share it with others, use external tools, or a front end to interact with it. However, you can also deploy to a variety of testnets from within Remix. Doing so will allow you to share your contract with others, at the cost of making it public.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Deploy a contract to the Base Sepolia testnet and interact with it in [BaseScan](https://sepolia.basescan.org/).
-
----
-
-## Prepare for Deployment
-
-Testnets operate in a similar, **but not exactly the same** manner as the main networks they shadow. You need a wallet with the appropriate token to interact with them by deploying a new contract or calling functions in a deployed contract.
-
-### Set Up a Wallet
-
-If you already have a wallet set up **exclusively for development**, you can skip to the next section. Otherwise, now is the time to jump in!
-
-
-It is very dangerous to use a wallet with valuable assets for development. You could easily write code with a bug that transfers the wrong amount of the wrong token to the wrong address. Transactions cannot be reversed once sent!
-
-Be safe and use separate wallets for separate purposes.
-
-
-
-First, add the [Coinbase](https://www.coinbase.com/wallet) or [Metamask](https://metamask.io/) wallet to your browser, and then [set up] a new wallet. As a developer, you need to be doubly careful about the security of your wallet! Many apps grant special powers to the wallet address that is the owner of the contract, such as allowing the withdrawal of all the Ether that customers have paid to the contract or changing critical settings.
-
-Once you've completed the wallet setup, enable developer settings and turn on testnets ([Coinbase Settings](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings), [Metamask Settings](https://support.metamask.io/hc/en-us/articles/13946422437147-How-to-view-testnets-in-MetaMask)).
-
-### Add Base Sepolia to your Wallet
-
-Use the [faucet](/base-chain/tools/network-faucets) to add Base Sepolia ETH to your wallet. You can also ask Base personnel on Discord or other social media for some!
-
-### Get Testnet Ether
-
-Testnet tokens have no real value, but the supply is not unlimited. You can use a faucet to get a small amount of Sepolia Ether to pay gas fees for testing. Most faucets allow you to ask for a small amount each day, and some won't send you more if your balance is too high.
-
-You can find many faucets by searching, and it's good to keep a few bookmarked because they have a tendency to go down from time to time. Faucet providers are constantly combating bad actors and sometimes need to disable their faucets while doing so.
-
-You can also access the [faucets on the web](https://coinbase.com/faucets).
-
-Once you have testnet Base Sepolia Ether, you can view your balance under the _Testnets_ tab in the Coinbase wallet or by selecting the testnet from the network dropdown in Metamask. Sadly, it's not actually worth the amount listed!
-
-
-
-
-
----
-
-## Deploying to Testnet
-
-Once you have testnet Ether, you can deploy your BasicMath contract!
-
-### Selecting the Environment
-
-Open the _Deploy & Run Transactions_ tab. Under _Environment_, select _Injected Provider_. It will list _Coinbase_, _Metamask_, or any other wallet you have activated here.
-
-
-
-
-
-If that option is not available, you can add it by choosing `Customize this list...`
-
-
-
-
-
-The first time you do this, your wallet will ask you to confirm that you want to connect this app (Remix) to your wallet.
-
-Once you are connected, you'll see the name of the network below the _Environment_ dropdown.
-
-
-
-
-
-For Base Sepolia, you should see `Custom (84532) network`. The old network, Goerli, was `84531`. If you don't see the correct network, change the active network in your wallet.
-
-### Deploy the Contract
-
-Click the orange _Deploy_ button. Because it costs gas to deploy a contract, you'll be asked to review and confirm a transaction.
-
-
-
-
-
-
-Always carefully review all transactions, confirming the transaction cost, assets transferred, and network. As a developer, you'll get used to approving transactions regularly. Do the best you can to avoid getting into the habit of clicking _Confirm_ without reviewing the transaction carefully. If you feel pressured to _Confirm_ before you run out of time, it is almost certainly a scam.
-
-
-
-After you click the _Confirm_ button, return to Remix and wait for the transaction to deploy. Copy its address and navigate to [`sepolia.basescan.org`](https://sepolia.basescan.org/).
-
-## Conclusion
-
-You now have the power to put smart contracts on the blockchain! You've only deployed to a test network, but the process for real networks is exactly the same - just more expensive!
-
----
diff --git a/docs/learn/deployment-to-testnet/deployment-to-testnet-exercise.mdx b/docs/learn/deployment-to-testnet/deployment-to-testnet-exercise.mdx
deleted file mode 100644
index 4ba3c4676..000000000
--- a/docs/learn/deployment-to-testnet/deployment-to-testnet-exercise.mdx
+++ /dev/null
@@ -1,36 +0,0 @@
----
-title: 'Deployment Exercise'
-sidebarTitle: 'Exercise'
-description: Exercise - Deploy your basic math contract and earn an NFT.
-hide_table_of_contents: false
----
-
-You've already built and deployed your [Basic Math](/learn/contracts-and-basic-functions/basic-functions-exercise) contract for this exercise. Now it's time to submit the address and earn an NFT pin to commemorate your accomplishment!
-
-
-We're currently in beta, so you'll only need to pay testnet funds to submit your contract, but this means you'll be getting a testnet NFT.
-
-Stay tuned for updates!
-
-
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/deployment-to-testnet/overview-of-test-networks-vid.mdx b/docs/learn/deployment-to-testnet/overview-of-test-networks-vid.mdx
deleted file mode 100644
index 2a6e1964c..000000000
--- a/docs/learn/deployment-to-testnet/overview-of-test-networks-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Overview of Test Networks
-description: Learn about test networks.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/deployment-to-testnet/test-networks.mdx b/docs/learn/deployment-to-testnet/test-networks.mdx
deleted file mode 100644
index 6b21c62af..000000000
--- a/docs/learn/deployment-to-testnet/test-networks.mdx
+++ /dev/null
@@ -1,97 +0,0 @@
----
-title: Test Networks
-description: An overview of Base test networks
-hide_table_of_contents: false
----
-
-This article provides a concise overview of Base test networks, highlighting their advantages, potential challenges, and comparing some of the most popular testnets.
-
----
-
-## Objectives:
-
-By the end of this lesson you should be able to:
-
-- Describe the uses and properties of the Base testnet
-- Compare and contrast Ropsten, Rinkeby, Goerli, and Sepolia
-
----
-
-## Why Testnets?
-
-As you dive into the development of smart contracts and onchain apps on Base, mainnet, or other networks, you'll need a safe, controlled, and efficient environment for testing and experimentation. Test networks, or testnets, serve as essential tools for you to test your smart contracts before deploying them to the mainnet, minimizing the risk of failures or vulnerabilities in live applications.
-
-By simulating the mainnet environment, testnets offer a realistic representation of real-world conditions, complete with network latency, gas fees, and other factors that impact the performance of smart contracts. This accurate representation enables you to identify potential issues, optimize your applications, and ensure the best possible user experience for your end-users. Moreover, testnets allow you to familiarize yourself with the Base ecosystem and gain valuable hands-on experience, making them indispensable tools for both seasoned developers and newcomers to the world of blockchain development.
-
-
-
-
-
----
-
-## The Advantages of Using Testnets
-
-Testnets offer several key advantages to developers:
-
-- **Real-time feedback:** Developers can quickly identify and fix errors or vulnerabilities in their smart contracts, ensuring robust and secure applications.
-
-- **No cost or risk:** Testnets use "fake" ether, enabling developers to deploy and interact with smart contracts without incurring any financial cost or risk.
-
-- **Easy accessibility:** Testnets are readily available for developers to join, allowing them to focus on development rather than infrastructure setup.
-
-- **Stress testing:** Testnets provide a suitable environment to stress test the Ethereum network infrastructure under various conditions. By simulating high transaction volumes, developers can evaluate how their applications perform under load and optimize them accordingly.
-
-- **Protocol upgrades and improvements:** Testnets allow developers to test new protocol updates, improvements, and potential forks before implementing them on the mainnet. This process helps identify any issues or incompatibilities and ensures a smoother transition to new features and optimizations.
-
----
-
-## Challenges Associated with Testnets
-
-While Ethereum testnets provide a valuable testing environment for developers, there are some challenges and limitations you should be aware of when using them:
-
-- **Network congestion:** Just like the mainnet, testnets can experience periods of network congestion due to high transaction volumes or other factors. During these periods, developers might face slow transaction processing times, which could impact their testing process and potentially delay development.
-
-- **Testnet instability:** Testnets may occasionally face downtime or network issues, which can disrupt the testing process. While these events are relatively rare, it's essential to be prepared for such occurrences and have a backup plan, such as switching to another testnet or using a local development environment.
-
-- **Differences in network behavior:** Although testnets simulate the mainnet environment, there might be subtle differences in network behavior, gas fees, or transaction processing times between the two. These differences could potentially impact the performance of your smart contracts on the mainnet. It's crucial to be aware of these discrepancies and make any necessary adjustments before deploying your smart contracts to the mainnet.
-
-- **Limited resources:** Testnet Ether is generally available for free through faucets, but these sources might have daily limits or other restrictions on the amount of testnet Ether that can be obtained. This limitation could affect your ability to perform extensive testing or carry out large-scale experiments on the testnet.
-
----
-
-## Popular Testnets
-
-Several well-known testnets have emerged over the years, each with its own set of features and benefits.
-
-
-
-
-
-### L1 Testnets
-
-- **Ropsten:** Ropsten played a significant role in Ethereum's history but was effectively deprecated by late 2022 when the Merge took place. The Merge marked the transition from proof-of-work to proof-of-stake consensus for the Ethereum mainnet. Ropsten's vulnerability to spam attacks and network instability made it unreliable for testing purposes.
-
-- **Rinkeby:** Rinkeby offered better security than Ropsten and used a proof-of-authority consensus mechanism. However, it lacked decentralization and client diversity, which ultimately led to its decline in popularity. After the Merge, Rinkeby is no longer a recommended test network.
-
-- **Goerli:** Launched in early 2019, Goerli initially utilized a multi-client proof-of-authority consensus model to improve stability and security. Following the Merge, it transitioned to a proof-of-stake consensus mechanism, maintaining its cross-client compatibility and making it an ideal choice for developers. As of January 2024, Goerli is being sunset in favor of Sepolia.
-
-- **Sepolia:** As one of the two original primary testnets alongside Goerli, Sepolia is designed for developers seeking a lighter weight chain for faster synchronization and interaction. As of January 2024, it is now the preferred testnet and developers should migrate to using it.
-
-### L2 Testnets
-
-- **Base Sepolia:** As new Layer-2 networks emerged that settled on Ethereum's Layer-1, the need for testnets dedicated to these L2 networks also arose. For instance, the L2 network Base has its own testnet, known as Base Sepolia. This testnet settles on the Ethereum Sepolia L1 testnet, providing an environment for testing L2-specific features and smart contracts.
-
-- **Optimism Sepolia:** Optimism, an Ethereum Layer-2 scaling solution utilizing Optimistic Rollups, has its own testnet called Optimism Sepolia. This testnet is also built on the Ethereum Sepolia L1 testnet and offers a testing environment for developers to experiment with Optimism's Layer-2 features, smart contracts, and apps.
-
----
-
-## Conclusion
-
-Ethereum and L2 testnets are essential for the safe and efficient development of smart contracts and apps, offering numerous advantages such as real-time feedback, cost-free testing, easy accessibility, stress testing, and protocol upgrade testing. Despite certain challenges associated with testnets, developers continue to rely on them for a robust testing environment. Among the various options, Sepolia has emerged as preferred choices for Ethereum developers due to enhanced security, stability, and strong community support. As the Ethereum ecosystem evolves, incorporating Layer-2 networks and other innovations, testnets will continue to play a crucial role in fostering blockchain development and contributing to the overall success and growth of the space.
-
----
-
-## See Also
-
-- [Networks](https://ethereum.org/en/developers/docs/networks/)
-- [The History of Ethereum Testnets](https://consensys.net/blog/news/the-history-of-ethereum-testnets/)
diff --git a/docs/learn/error-triage/error-triage-exercise-source.sol b/docs/learn/error-triage/error-triage-exercise-source.sol
deleted file mode 100644
index 2544a99ec..000000000
--- a/docs/learn/error-triage/error-triage-exercise-source.sol
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-contract ErrorTriageExercise {
- /**
- * Finds the difference between each uint with it's neighbor (a to b, b to c, etc.)
- * and returns a uint array with the absolute integer difference of each pairing.
- */
- function diffWithNeighbor(
- uint _a,
- uint _b,
- uint _c,
- uint _d
- ) public pure returns (uint[] memory) {
- uint[] memory results = new uint[](3);
-
- results[0] = _a - _b;
- results[1] = _b - _c;
- results[2] = _c - _d;
-
- return results;
- }
-
- /**
- * Changes the _base by the value of _modifier. Base is always >= 1000. Modifiers can be
- * between positive and negative 100;
- */
- function applyModifier(
- uint _base,
- int _modifier
- ) public pure returns (uint) {
- return _base + _modifier;
- }
-
- /**
- * Pop the last element from the supplied array, and return the popped
- * value (unlike the built-in function)
- */
- uint[] arr;
-
- function popWithReturn() public returns (uint) {
- uint index = arr.length - 1;
- delete arr[index];
- return arr[index];
- }
-
- // The utility functions below are working as expected
- function addToArr(uint _num) public {
- arr.push(_num);
- }
-
- function getArr() public view returns (uint[] memory) {
- return arr;
- }
-
- function resetArr() public {
- delete arr;
- }
-}
diff --git a/docs/learn/error-triage/error-triage-exercise.mdx b/docs/learn/error-triage/error-triage-exercise.mdx
deleted file mode 100644
index e11b47d25..000000000
--- a/docs/learn/error-triage/error-triage-exercise.mdx
+++ /dev/null
@@ -1,97 +0,0 @@
----
-sidebarTitle: Exercise
-title: Error Triage Exercise
-description: Exercise - Demonstrate your debugging skill.
-hide_table_of_contents: false
----
-
-Copy the starter code into a new file in Remix.
-
-Debug the existing functions in the provided contract.
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-contract ErrorTriageExercise {
- /**
- * Finds the difference between each uint with it's neighbor (a to b, b to c, etc.)
- * and returns a uint array with the absolute integer difference of each pairing.
- */
- function diffWithNeighbor(
- uint _a,
- uint _b,
- uint _c,
- uint _d
- ) public pure returns (uint[] memory) {
- uint[] memory results = new uint[](3);
-
- results[0] = _a - _b;
- results[1] = _b - _c;
- results[2] = _c - _d;
-
- return results;
- }
-
- /**
- * Changes the _base by the value of _modifier. Base is always >= 1000. Modifiers can be
- * between positive and negative 100;
- */
- function applyModifier(
- uint _base,
- int _modifier
- ) public pure returns (uint) {
- return _base + _modifier;
- }
-
- /**
- * Pop the last element from the supplied array, and return the popped
- * value (unlike the built-in function)
- */
- uint[] arr;
-
- function popWithReturn() public returns (uint) {
- uint index = arr.length - 1;
- delete arr[index];
- return arr[index];
- }
-
- // The utility functions below are working as expected
- function addToArr(uint _num) public {
- arr.push(_num);
- }
-
- function getArr() public view returns (uint[] memory) {
- return arr;
- }
-
- function resetArr() public {
- delete arr;
- }
-}
-
-```
-
----
-
-## Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/error-triage/error-triage-vid.mdx b/docs/learn/error-triage/error-triage-vid.mdx
deleted file mode 100644
index c8435b89b..000000000
--- a/docs/learn/error-triage/error-triage-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Error Triage
-description: Learn to debug common errors.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/error-triage/error-triage.mdx b/docs/learn/error-triage/error-triage.mdx
deleted file mode 100644
index 590ff7d9c..000000000
--- a/docs/learn/error-triage/error-triage.mdx
+++ /dev/null
@@ -1,449 +0,0 @@
----
-title: Error Triage
-sidebarTitle: Error Guide
-description: Learn how to identify and resolve common errors in Solidity.
-hide_table_of_contents: false
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Debug common solidity errors including transaction reverted, out of gas, stack overflow, value overflow/underflow, index out of range, etc.
-
----
-
-## Compiler Errors
-
-Compiler errors are manifold but almost always very easy to debug, since the error message usually tells you what is wrong and how to fix it.
-
-### Type Errors
-
-You will get a compiler error if you try to assign a literal to the wrong type.
-
-```solidity
-// Bad code example, do not use
-function compilerTypeError() public pure returns (uint) {
- uint myNumber = "One";
- return myNumber;
-}
-```
-
-```text
-from solidity:
-TypeError: Type literal_string "One" is not implicitly convertible to expected type uint256.
- --> contracts/ErrorTriage.sol:8:9:
- |
-8 | uint myNumber = "One";
- | ^^^^^^^^^^^^^^^^^^^^^
-```
-
-Fix by correcting the type or value, as appropriate for your needs:
-
-
-
-
-```solidity
-function compilerTypeErrorFixed() public pure returns (string) {
- string myNumber = "One";
- return myNumber;
-}
-```
-
-
-
-### Conversion Errors
-
-Conversion errors occur when you attempt to _implicitly_ convert one type to another. Solidity only allows this under very narrow circumstances where there is no possibility of ambiguous interpretation of the data.
-
-```solidity
-// Bad code example, do not use
-function compilerConversionError() public pure returns (uint) {
- int8 first = 1;
-
- return first;
-}
-```
-
-```text
-from solidity:
-TypeError: Return argument type int8 is not implicitly convertible to expected type (type of first return variable) uint256.
- --> contracts/ErrorTriage.sol:15:16:
- |
-15 | return first;
- | ^^^^^
-```
-
-Fix by explicitly casting as necessary:
-
-
-
-
-```solidity
-function compilerConversionErrorFixed() public pure returns (uint) {
- int8 first = 1;
-
- return uint(uint8(first));
-}
-```
-
-
-
-
-You'll commonly need to use multiple conversions to bridge from one type to another.
-
-
-
-### Operator Errors
-
-You cannot use operators between types as flexibly as you may be used to.
-
-```solidity
-// Bad code example, do not use
-function compilerOperatorError() public pure returns (uint) {
- int8 first = 1;
- uint256 second = 2;
-
- uint sum = first + second;
-
- return sum;
-}
-```
-
-Operator errors are often paired with a type error.
-
-```text
-from solidity:
-TypeError: Operator + not compatible with types int8 and uint256.
- --> contracts/ErrorTriage.sol:22:20:
- |
-22 | uint sum = first + second;
- | ^^^^^^^^^^^^^^
-
-from solidity:
-TypeError: Type int8 is not implicitly convertible to expected type uint256.
- --> contracts/ErrorTriage.sol:22:9:
- |
-22 | uint sum = first + second;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
-```
-
-Resolve by explicitly converting to the final type:
-
-
-
-
-```
-function compilerOperatorErrorFixed() public pure returns (uint) {
- int8 first = 1;
- uint256 second = 2;
-
- uint sum = uint(uint8(first)) + second;
-
- return sum;
-}
-```
-
-
-
-### Stack Depth Limit
-
-The [EVM stack] has 1024 slots, but only the top 16 slots are accessible. As a result, you can only have fewer than 16 variables in scope at one time.
-
-
-Other items can also use up these slots. You are **not** guaranteed 15 slots, it can be lower.
-
-
-
-```solidity
-// Bad code example, do not use
-function stackDepthLimit() public pure returns (uint) {
- uint first = 1;
- uint second = 2;
- uint third = 3;
- uint fourth = 4;
- uint fifth = 5;
- uint sixth = 6;
- uint seventh = 7;
- uint eighth = 8;
- uint ninth = 9;
- uint tenth = 10;
- uint eleventh = 11;
- uint twelfth = 12;
- uint thirteenth = 13;
- uint fourteenth = 14;
- uint fifteenth = 15;
- uint sixteenth = 16;
-
- return first +
- second +
- third +
- fourth +
- fifth +
- sixth +
- seventh +
- eighth +
- ninth +
- tenth +
- eleventh +
- twelfth +
- thirteenth +
- fourteenth +
- fifteenth +
- sixteenth;
- }
-```
-
-```text
-from solidity:
-CompilerError: Stack too deep. Try compiling with --via-ir (cli) or the equivalent viaIR: true (standard JSON) while enabling the optimizer. Otherwise, try removing local variables.
- --> contracts/ErrorTriage.sol:92:17:
- |
-92 | eighth +
- | ^^^^^^
-```
-
-Resolve this error by breaking up large functions and separating operations into different levels of scope.
-
-
-
-
-```solidity
-function stackDepthLimitFixed() public pure returns (uint) {
- uint subtotalA;
- {
- uint first = 1;
- uint second = 2;
- uint third = 3;
- uint fourth = 4;
- uint fifth = 5;
- uint sixth = 6;
- uint seventh = 7;
- uint eighth = 8;
- subtotalA = first +
- second +
- third +
- fourth +
- fifth +
- sixth +
- seventh +
- eighth;
- }
-
- uint subtotalB;
- {
- uint ninth = 9;
- uint tenth = 10;
- uint eleventh = 11;
- uint twelfth = 12;
- uint thirteenth = 13;
- uint fourteenth = 14;
- uint fifteenth = 15;
- uint sixteenth = 16;
- subtotalB = ninth +
- tenth +
- eleventh +
- twelfth +
- thirteenth +
- fourteenth +
- fifteenth +
- sixteenth;
- }
-
- return subtotalA + subtotalB;
-}
-```
-
-
-
----
-
-## Logical Errors
-
-Logical errors occur when your code is syntactically correct, but still results in a data state that is a violation of the rules of the language.
-
-A [panic] occurs when your code tries to do an illegal operation. These return with a very basic error code, which Remix unfortunately hides. However, it makes up for that annoyance by providing a very powerful debugger.
-
-
-The Remix VM doesn't behave exactly the same as true onchain operations, so note that these errors will not behave exactly the same if triggered while testing with Hardhat, or called from a front end.
-
-caution
-
-For each of these examples, copy them into Remix to explore with the debugger on your own.
-
-### Array Index Out-of-Bounds
-
-A panic will be triggered if you try to access an array at an invalid index.
-
-```solidity
-// Bad code example, do not use
-function badGetLastValue() public pure returns (uint) {
- uint[4] memory arr = [uint(1), 2, 3, 4];
-
- return arr[arr.length];
-}
-```
-
-Running this function will result in the following error in the console:
-
-```text
-call to ErrorTriage.badGetLastValue errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-Note: The called function should be payable if you send value and the value you send should be less than your current balance.
-Debug the transaction to get more information.
-```
-
-Click the _Debug_ button to open the debugger.
-
-
-
-
-
-The debugger contains panels with information about variables in storage, memory, what's on the stack, and so on. You can also add breakpoints to lines of code to further help with debugging.
-
-One of the most useful features is the link near the top instructing you to _"Click here to jump where the call reverted."_
-
-Click that link and the debugger will jump to the point of failure, **and highlight the code that caused the panic.** Neat!
-
-
-
-
-
-You can find the specific error here, but it's difficult.
-
-Look in the _Memory_ panel. The first item at `0x0` has a hash starting with `0x4e487b71`. This code indicates a panic.
-
-The second item, at `0x20` has the error code of `32` hidden in it, which is for array out-of-bounds.
-
-
-
-
-
-It's sometimes better to just review the code first to see if the error is obvious.
-
-```solidity
-function badGetLastValueFixed() public pure returns (uint) {
- uint[4] memory arr = [uint(1), 2, 3, 4];
-
- return arr[arr.length-1];
-}
-```
-
-### Out of Gas
-
-The default settings for Remix make it difficult to trigger an out of gas error because the VM will often crash first. For this example, go to the _Deploy & Run Transactions_ tab and reduce the gas limit to **300000**.
-
-If you write code that can have an ambiguous execution time, it becomes very difficult to accurately estimate gas limits.
-
-In this example, each loop has a 1 in 1000 chance of ending.
-
-
-`block.timestamp` can be manipulated. **DO NOT** use this as a source of randomness if any value can be derived from one outcome over another!
-
-
-
-```solidity
-// Bad code example, do not use
-function badRandomLoop() public view returns (uint) {
- uint seed = 0;
- // DO NOT USE THIS METHOD FOR RANDOM NUMBERS!!! IT IS EASILY EXPLOITABLE!!!
- while(uint(keccak256(abi.encodePacked(block.timestamp, seed))) % 1000 != 0) {
- seed++;
- // ...do something
- }
-
- return seed;
-}
-```
-
-Run this function a few times. Often, it will work just fine. Other times, an error appears:
-
-```text
-call to ErrorTriage.badLoop errored: VM error: out of gas.
-
-out of gas
- The transaction ran out of gas. Please increase the Gas Limit.
-
-Debug the transaction to get more information.
-```
-
-The error message here is a bit misleading. You do **not** usually want to fix this by increasing the gas limit. If you're getting a gas error because the transaction didn't estimate for enough gas, it's better to refactor for better predictability.
-
-```solidity
-function badRandomLoopFixed() public view returns (uint) {
- // DO NOT USE THIS METHOD FOR RANDOM NUMBERS!!! IT IS EASILY EXPLOITABLE!!!
- uint times = uint(keccak256(abi.encodePacked(block.timestamp))) % 1000;
-
- for(uint i = 0; i <= times; i++) {
- // ...do something
- }
-
- return times;
-}
-```
-
-### Overflow or Underflow
-
-The `uint` type will _panic_ in the event of an overflow or underflow.
-
-```solidity
-function badSubtraction() public pure returns (uint) {
- uint first = 1;
- uint second = 2;
- return first - second;
-}
-```
-
-As before, you can see the panic code and panic type in _memory_.
-
-
-
-
-
-In this case, the error type is `11`, for overflow/underflow outside of an `unchecked` block.
-
-Fix by changing your code to handle the expected range of values.
-
-
-
-
-```solidity
-function badSubstractionFixed() public pure returns (int) {
- int first = 1;
- int second = 2;
- return first - second;
-}
-```
-
-
-
-### Divide by Zero
-
-Divide by zero errors also trigger a panic, with a code of `12`.
-
-```solidity
-function badDivision() public pure returns (uint) {
- uint first = 1;
- uint second = 0;
- return first / second;
-}
-```
-
-
-
-
-
-Don't divide by zero.
-
----
-
-## Conclusion
-
-In this lesson, you reviewed the causes of and solutions for a number of compiler errors and logical errors that you may encounter.
-
----
-
-[panic]: https://docs.soliditylang.org/en/v0.8.17/control-structures.html?#panic-via-assert-and-error-via-require
-[EVM stack]: https://docs.soliditylang.org/en/v0.8.17/introduction-to-smart-contracts.html#storage-memory-and-the-stack
diff --git a/docs/learn/events/hardhat-events-sbs.mdx b/docs/learn/events/hardhat-events-sbs.mdx
deleted file mode 100644
index 9691cd39c..000000000
--- a/docs/learn/events/hardhat-events-sbs.mdx
+++ /dev/null
@@ -1,285 +0,0 @@
----
-title: Events
-sidebarTitle: Step by Step Guide
-description: Events in Solidity
-hide_table_of_contents: false
----
-
-In this article, you'll learn how events work in Solidity by reviewing some practical examples and common use cases of events.
-
-
-This tutorial has been moved as part of a reorganization! It assumes you are using Hardhat. Everything in this lesson will work with minor adjustments if you are working in Foundry or Remix.
-
-
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- Write and trigger an event
-- List common uses of events
-- Understand events vs. smart contract storage
-
----
-
-## Overview
-
-Understanding how Solidity events work is important in the world of smart contract development. Events provide a powerful way to create event-driven applications on the blockchain. They allow you to notify external parties, such as off-chain applications, user interfaces, and any entity that wants to listen for events of a particular contract.
-
-In this tutorial, you'll learn how to declare, trigger, and utilize events, gaining the knowledge necessary to enhance the functionality and user experience of your decentralized applications.
-
-## What are events?
-
-From the official solidity documentation, [events] are:
-
-> _...an abstraction on top of the EVM’s logging functionality. Applications can subscribe and listen to these events through the RPC interface of an Ethereum client._
-
-> _...when you call them, they cause the arguments to be stored in the transaction’s log – a special data structure in the blockchain. These logs are associated with the address of the contract that emitted them, are incorporated into the blockchain, and stay there as long as a block is accessible (forever as of now, but this might change in the future)._
-
-In other words, events are an abstraction that allow you to store a transaction's log information in the blockchain.
-
-## Your first solidity event
-
-Start by creating a first event in the `Lock.sol` contract that's included by default in Hardhat.
-
-The event is called `Created` and includes the address of the creator and the amount that was sent during the creation of the smart contract. Then, `emit` the event in the constructor:
-
-```solidity
-emit Created(msg.sender, msg.value);
-```
-
-The contract is:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Lock {
- uint public unlockTime;
- address payable public owner;
-
- event Created(address owner, uint amount);
-
- constructor(uint _unlockTime) payable {
- require(
- block.timestamp < _unlockTime,
- "Unlock time should be in the future"
- );
-
- unlockTime = _unlockTime;
- owner = payable(msg.sender);
-
- emit Created(msg.sender, msg.value);
- }
-}
-```
-
-Events can be defined at the file level or as inheritable members of contracts (including interfaces). You can also define the event in an interface as:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-interface ILock {
- event Created(address owner, uint amount);
-}
-
-contract Lock is ILock {
- uint public unlockTime;
- address payable public owner;
-
- constructor(uint _unlockTime) payable {
- require(
- block.timestamp < _unlockTime,
- "Unlock time should be in the future"
- );
-
- unlockTime = _unlockTime;
- owner = payable(msg.sender);
-
- emit Created(msg.sender, msg.value);
- }
-}
-```
-
-You can test the event by simplifying the original test file with the following code:
-
-```solidity
-import {
- time,
-} from "@nomicfoundation/hardhat-toolbox/network-helpers";
-import { ethers } from "hardhat";
-
-describe("Lock tests", function () {
- describe("Deployment", function () {
- it("Should set the right unlockTime", async function () {
- const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
- const ONE_GWEI = 1_000_000_000;
-
- const lockedAmount = ONE_GWEI;
- const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;
-
- // Contracts are deployed using the first signer/account by default
- const [owner] = await ethers.getSigners();
-
- // But we do it explicit by using the owner signer
- const LockFactory = await ethers.getContractFactory("Lock", owner);
- const lock = await LockFactory.deploy(unlockTime, { value: lockedAmount });
-
- const hash = await lock.deploymentTransaction()?.hash
- const receipt = await ethers.provider.getTransactionReceipt(hash as string)
-
- console.log("Sender Address", owner.address)
- console.log("Receipt.logs", receipt?.logs)
-
- const defaultDecoder = ethers.AbiCoder.defaultAbiCoder()
- const decodedData = defaultDecoder.decode(['address', 'uint256'], receipt?.logs[0].data as string)
- console.log("decodedData", decodedData)
- });
- });
-});
-```
-
-Notice that the previous code is logging the sender address and the logs coming from the transaction receipt. You are also decoding the `receipts.logs[0].data` field that contains the information emitted by the event but not in a human-readable way, since it is encoded. For that reason, you can use `AbiCoder` to decode the raw data.
-
-By running `npx hardhat test`, you should be able to see the following:
-
-```solidity
- Lock tests
- Deployment
-Sender Address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
-Receipt.logs [
- Log {
- provider: HardhatEthersProvider {
- _hardhatProvider: [LazyInitializationProviderAdapter],
- _networkName: 'hardhat',
- _blockListeners: [],
- _transactionHashListeners: Map(0) {},
- _eventListeners: []
- },
- transactionHash: '0xad4ff104036f23096ea5ed165bff1c3e1bc0f53e375080f84bce4cc108c28cee',
- blockHash: '0xb2117cfd2aa8493a451670acb0ce14228b06d17bf545cd7efad6791aeac83c05',
- blockNumber: 1,
- removed: undefined,
- address: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
- data: '0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000003b9aca00',
- topics: [
- '0x0ce3610e89a4bb9ec9359763f99110ed52a4abaea0b62028a1637e242ca2768b'
- ],
- index: 0,
- transactionIndex: 0
- }
-]
-decodedData Result(2) [ '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', 1000000000n ]
- ✔ Should set the right unlockTime (1008ms)
-```
-
-Notice the value `f39fd6e51aad88f6f4ce6ab8827279cfffb92266` is encoded in the property data, which is the address of the sender.
-
-## Event topics
-
-Another important feature is that events can be indexed by adding the indexed attribute to the event declaration.
-
-For example, if you modify the interface with:
-
-```solidity
-interface ILock {
- event Created(address indexed owner, uint amount);
-}
-```
-
-Then, if you run `npx hardhat test` again, an error may occur because the decoding assumes that the data field contains an `address` and a `uint256`. But by adding the indexed attribute, you are instructing that the events will be added to a special data structure known as "topics". Topics have some limitations, since the maximum indexed attributes can be up to three parameters and a topic can only hold a single word (32 bytes).
-
-You then need to modify the decoding line in the test file with the following:
-
-```solidity
-const decodedData = defaultDecoder.decode(['uint256'], receipt?.logs[0].data as string)
-```
-
-Then, you should be able to see the receipt as:
-
-```solidity
- Lock tests
- Deployment
-Sender Address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
-Receipt.logs [
- Log {
- provider: HardhatEthersProvider {
- _hardhatProvider: [LazyInitializationProviderAdapter],
- _networkName: 'hardhat',
- _blockListeners: [],
- _transactionHashListeners: Map(0) {},
- _eventListeners: []
- },
- transactionHash: '0x0fd52fd72bca26879474d3e512fb812489111a6654473fd288c6e8ec0432e09d',
- blockHash: '0x138f74df5637315099d31aedf5bf643cf95c2bb7ae923c21fcd7f0075cb55324',
- blockNumber: 1,
- removed: undefined,
- address: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
- data: '0x000000000000000000000000000000000000000000000000000000003b9aca00',
- topics: [
- '0x0ce3610e89a4bb9ec9359763f99110ed52a4abaea0b62028a1637e242ca2768b',
- '0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266'
- ],
- index: 0,
- transactionIndex: 0
- }
-]
-decodedData Result(1) [ 1000000000n ]
- ✔ Should set the right unlockTime (994ms)
-```
-
-Notice the topics property, which now contains the address of the sender: `f39fd6e51aad88f6f4ce6ab8827279cfffb92266`.
-
-## Common uses of events
-
-Solidity events have several common use cases, which are described in the following sections.
-
-### User notifications
-
-Events can be used to notify users or external systems about certain contract actions.
-
-### Logging
-
-Events are primarily used to log significant changes within the contract, providing a transparent and verifiable history of what has occurred.
-
-### Historical state reconstruction
-
-Events can be valuable for recreating the historical state of a contract. By capturing and analyzing emitted event logs, you can reconstruct past states, offering a transparent and auditable history of the contract's actions and changes.
-
-### Debugging and monitoring
-
-Events are essential for debugging and monitoring contract behavior, as they provide a way to observe what's happening on the blockchain.
-
-The ability to use events to recreate historical states provides an important auditing and transparency feature, allowing users and external parties to verify the contract's history and actions. While not a common use, it's a powerful capability that can be particularly useful in certain contexts.
-
-## Events vs. smart contract storage
-
-Although it is possible to rely on events to fully recreate the state of a particular contract, there are a few other options to consider.
-
-Existing services such as [The Graph] allow you to index and create GraphQL endpoints for your smart contracts and generate entities based on custom logic. However, you must pay for that service since you are adding an intermediate layer to your application. This has the following benefits, such as:
-
-- the ability to simply query one particular endpoint to get all the information you need
-- your users will pay less gas costs due to the minimization of storage usage in your contract
-
-But storing all of the information within the smart contract and relying fully on it to access data can create more complexity, since not all of the data is directly query-able. The benefits of this approach include:
-
-- your application requires only the smart contract address to access all of the required data
-- there are fewer dependencies involved, which makes this approach more crypto native in the sense that everything is in the blockchain (but, storing all the data in the blockchain will cause higher gas costs)
-
-As a smart contract developer, you must evaluate which options work best for you.
-
-## Conclusion
-
-In this lesson, you've learned the basics of Solidity events and their importance in Ethereum smart contract development. You now understand how to declare and trigger events, a few of their common use cases, and the difference between events and smart contract storage.
-
-Now that you have a solid grasp of events and their versatile applications, you can leverage them to build more sophisticated and interactive smart contracts that meet your specific needs, all while being mindful of the cost considerations.
-
----
-
-## See also
-
-[events]: https://docs.soliditylang.org/en/latest/contracts.html#events
-[The Graph]: https://thegraph.com/
diff --git a/docs/learn/exercise-contracts.mdx b/docs/learn/exercise-contracts.mdx
deleted file mode 100644
index dfab125a5..000000000
--- a/docs/learn/exercise-contracts.mdx
+++ /dev/null
@@ -1,27 +0,0 @@
----
-title: "Exercise Contracts"
-description: A list of verified unit test contracts for Base Learn exercises.
----
-
-Many of the sections in Base Learn contain an exercise to test your knowledge on the material you have just completed. We tell you **what** to do, but not **how** to do it. You have to apply your knowledge and demonstrate the new abilities you have earned.
-
-Upon success, you'll be granted a non-transferable, or soulbound, NFT as a memento of your learning. You can track your progress on the [progress page].
-
-Below is a list of the exercises, with links to view their code. The unit tests are written in a bespoke framework in Solidity, but the patterns should be recognizable to most engineers.
-
-| Exercise | Code |
-| :--------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- |
-| [Deploying to a Testnet](/learn/deployment-to-testnet/deployment-to-testnet-exercise) | [0x075eB9Dc52177Aa3492E1D26f0fDE3d729625d2F](https://sepolia.basescan.org/address/0x075eb9dc52177aa3492e1d26f0fde3d729625d2f#code#F16#L1) |
-| [Control Structures](/learn/control-structures/control-structures-exercise) | [0xF4D953A3976F392aA5509612DEfF395983f22a84](https://sepolia.basescan.org/address/0xf4d953a3976f392aa5509612deff395983f22a84#code#F17#L1) |
-| [Storage](/learn/storage/storage-exercise) | [0x567452C6638c0D2D9778C20a3D59749FDCaa7aB3](https://sepolia.basescan.org/address/0x567452c6638c0d2d9778c20a3d59749fdcaa7ab3#code#F17#L1) |
-| [Arrays](/learn/arrays/arrays-exercise) | [0x5B0F80cA6f5bD60Cc3b64F0377f336B2B2A56CdF](https://sepolia.basescan.org/address/0x5b0f80ca6f5bd60cc3b64f0377f336b2b2a56cdf) |
-| [Mappings](/learn/mappings/mappings-exercise) | [0xD32E3ACe3272e2037003Ca54CA7E5676f9b8D06C](https://sepolia.basescan.org/address/0xd32e3ace3272e2037003ca54ca7e5676f9b8d06c#code#F17#L1) |
-| [Structs](/learn/structs/structs-exercise) | [0x9eB1Fa4cD9bd29ca2C8e72217a642811c1F6176d](https://sepolia.basescan.org/address/0x9eb1fa4cd9bd29ca2c8e72217a642811c1f6176d#code#F17#L1) |
-| [Inheritance](/learn/inheritance/inheritance-exercise) | [0xF90dA05e77a33Fe6D64bc2Df84e7dd0069A2111C](https://sepolia.basescan.org/address/0xF90dA05e77a33Fe6D64bc2Df84e7dd0069A2111C#code#F17#L1) |
-| [Imports](/learn/imports/imports-exercise) | [0x8dD188Ec36084D59948F90213AFCd04429E33c0c](https://sepolia.basescan.org/address/0x8dd188ec36084d59948f90213afcd04429e33c0c#code#F17#L1) |
-| [Errors](/learn/error-triage/error-triage-exercise) | [0xC1BD0d9A8863f2318001BC5024c7f5F58a2236F7](https://sepolia.basescan.org/address/0xc1bd0d9a8863f2318001bc5024c7f5f58a2236f7#code#F17#L1) |
-| [The "new" Keyword](/learn/new-keyword/new-keyword-exercise) | [0x4f21e69d0CDE8C21cF82a6b37Dda5444716AFA46](https://sepolia.basescan.org/address/0x4f21e69d0cde8c21cf82a6b37dda5444716afa46#code#F17#L1) |
-| [Minimal Tokens](/learn/token-development/minimal-tokens/minimal-tokens-exercise) | [0x10Ce928030E136EcC74d4a4416Db9b533e3c694D](https://sepolia.basescan.org/address/0x10ce928030e136ecc74d4a4416db9b533e3c694d#code#F17#L1) |
-| [ERC-20 Tokens](/learn/token-development/erc-20-token/erc-20-exercise) | [0x4F333c49B820013e5E6Fe86634DC4Da88039CE50](https://sepolia.basescan.org/address/0x4f333c49b820013e5e6fe86634dc4da88039ce50#code#F21#L1) |
-| [ERC-721 Tokens](/learn/token-development/erc-721-token/erc-721-exercise) | [0x15534ED3d1dBA55148695B2Ba4164F147E47a10c](https://sepolia.basescan.org/address/0x15534ed3d1dba55148695b2ba4164f147e47a10c#code#F18#L1) |
-
diff --git a/docs/learn/foundry/deploy-with-foundry.mdx b/docs/learn/foundry/deploy-with-foundry.mdx
deleted file mode 100644
index 1bd456ac1..000000000
--- a/docs/learn/foundry/deploy-with-foundry.mdx
+++ /dev/null
@@ -1,307 +0,0 @@
----
-title: Deploying a smart contract using Foundry
-slug: /deploy-with-foundry
-description: "A tutorial that teaches how to deploy a smart contract on the Base test network using Foundry. Includes instructions for setting up the environment, compiling, and deploying the smart contract."
-author: neodaoist
----
-
-# Deploying a smart contract using Foundry
-
-This article will provide an overview of the [Foundry](https://book.getfoundry.sh/) development toolchain, and show you how to deploy a contract to **Base Sepolia** testnet.
-
-Foundry is a powerful suite of tools to develop, test, and debug your smart contracts. It comprises several individual tools:
-
-- `forge`: the main workhorse of Foundry — for developing, testing, compiling, and deploying smart contracts
-- `cast`: a command-line tool for performing Ethereum RPC calls (e.g., interacting with contracts, sending transactions, and getting onchain data)
-- `anvil`: a local testnet node, for testing contract behavior from a frontend or over RPC
-- `chisel`: a Solidity [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), for trying out Solidity snippets on a local or forked network
-
-Foundry offers extremely fast feedback loops (due to the under-the-hood Rust implementation) and less context switching — because you'll be writing your contracts, tests, and deployment scripts **All** in Solidity!
-
-
-For production / mainnet deployments the steps below in this tutorial will be almost identical, however, you'll want to ensure that you've configured `Base` (mainnet) as the network rather than `Base Sepolia` (testnet).
-
-
-## Objectives
-
-By the end of this tutorial, you should be able to do the following:
-
-- Setup Foundry for Base
-- Create an NFT smart contract for Base
-- Compile a smart contract for Base (using `forge`)
-- Deploy a smart contract to Base (also with `forge`)
-- Interact with a smart contract deployed on Base (using `cast`)
-
-## Prerequisites
-
-### Foundry
-
-This tutorial requires you have Foundry installed.
-
-- From the command-line (terminal), run: `curl -L https://foundry.paradigm.xyz | bash`
-- Then run `foundryup`, to install the latest (nightly) build of Foundry
-
-For more information, see the Foundry Book [installation guide](https://book.getfoundry.sh/getting-started/installation).
-
-### Coinbase Wallet
-
-In order to deploy a smart contract, you will first need a web3 wallet. You can create a wallet by downloading the Coinbase Wallet browser extension.
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-Deploying contracts to the blockchain requires a gas fee. Therefore, you will need to fund your wallet with ETH to cover those gas fees.
-
-For this tutorial, you will be deploying a contract to the Base Sepolia test network. You can fund your wallet with Base Sepolia ETH using one of the faucets listed on the Base [Network Faucets](https://docs.base.org/base-chain/tools/network-faucets) page.
-
-## Creating a project
-
-Before you can begin deploying smart contracts to Base, you need to set up your development environment by creating a Foundry project.
-
-To create a new Foundry project, first create a new directory:
-
-```bash
-mkdir myproject
-```
-
-Then run:
-
-```bash
-cd myproject
-forge init
-```
-
-This will create a Foundry project, which has the following basic layout:
-
-```bash
-.
-├── foundry.toml
-├── script
- │ └── Counter.s.sol
-├── src
- │ └── Counter.sol
-└── test
- └── Counter.t.sol
-```
-
-## Compiling the smart contract
-
-Below is a simple NFT smart contract ([ERC-721](https://eips.ethereum.org/EIPS/eip-721)) written in the Solidity programming language:
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.23;
-
-import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";
-
-contract NFT is ERC721 {
- uint256 public currentTokenId;
-
- constructor() ERC721("NFT Name", "NFT") {}
-
- function mint(address recipient) public payable returns (uint256) {
- uint256 newItemId = ++currentTokenId;
- _safeMint(recipient, newItemId);
- return newItemId;
- }
-}
-```
-
-The Solidity code above defines a smart contract named `NFT`. The code uses the `ERC721` interface provided by the [OpenZeppelin Contracts library](https://docs.openzeppelin.com/contracts/5.x/) to create an NFT smart contract. OpenZeppelin allows developers to leverage battle-tested smart contract implementations that adhere to official ERC standards.
-
-To add the OpenZeppelin Contracts library to your project, run:
-
-```bash
-forge install openzeppelin/openzeppelin-contracts
-```
-
-In your project, delete the `src/Counter.sol` contract that was generated with the project and add the above code in a new file called `src/NFT.sol`. (You can also delete the `test/Counter.t.sol` and `script/Counter.s.sol` files, but you should add your own tests ASAP!).
-
-To compile our basic NFT contract using Foundry, run:
-
-```bash
-forge build
-```
-
-## Configuring Foundry with Base
-
-Next, you will configure your Foundry project to deploy smart contracts to the Base network. First you'll store your private key in an encrypted keystore, then you'll add Base as a network.
-
-### Storing your private key
-
-The following command will import your private key to Foundry's secure keystore. You will be prompted to enter your private key, as well as a password for signing transactions:
-
-```bash
-cast wallet import deployer --interactive
-```
-
-
-For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key). **It is critical that you do NOT commit this to a public repo**.
-
-
-Run this command to confirm that the 'deployer' account is setup in Foundry:
-
-```bash
-cast wallet list
-```
-
-### Adding Base as a network
-
-When verifying a contract with BaseScan, you need an API key. You can get your BaseScan API key from [here](https://basescan.org/myapikey) after you sign up for an account.
-
-
-Although they're made by the same folks, Etherscan API keys will **not** work on BaseScan!
-
-
-Now create a `.env` file in the home directory of your project to add the Base network and an API key for verifying your contract on BaseScan:
-
-```
-BASE_MAINNET_RPC="https://mainnet.base.org"
-BASE_SEPOLIA_RPC="https://sepolia.base.org"
-ETHERSCAN_API_KEY=""
-```
-
-Note that even though you're using BaseScan as your block explorer, Foundry expects the API key to be defined as `ETHERSCAN_API_KEY`.
-
-### Loading environment variables
-
-Now that you've created the above `.env` file, run the following command to load the environment variables in the current command line session:
-
-```bash
-source .env
-```
-
-## Deploying the smart contract
-
-With your contract compiled and your environment configured, you are ready to deploy to the Base Sepolia test network!
-
-Today, you'll use the `forge create` command, which is a straightforward way to deploy a single contract at a time. In the future, you may want to look into [`forge script`](https://book.getfoundry.sh/guides/scripting-with-solidity), which enables scripting onchain transactions and deploying more complex smart contract projects.
-
-You'll need testnet ETH in your wallet. See the [prerequisites](#prerequisites) if you haven't done that yet. Otherwise, the deployment attempt will fail.
-
-To deploy the contract to the Base Sepolia test network, run the following command. You will be prompted to enter the password that you set earlier, when you imported your private key:
-
-```bash
-forge create ./src/NFT.sol:NFT --rpc-url $BASE_SEPOLIA_RPC --account deployer
-```
-
-The contract will be deployed on the Base Sepolia test network. You can view the deployment status and contract by using a [block explorer](/base-chain/tools/block-explorers) and searching for the address returned by your deploy script. If you've deployed an exact copy of the NFT contract above, it will already be verified and you'll be able to read and write to the contract using the web interface.
-
-
-If you'd like to deploy to mainnet, you'll modify the command like so:
-
-
-```bash
-forge create ./src/NFT.sol:NFT --rpc-url $BASE_MAINNET_RPC --account deployer
-```
-
-Regardless of the network you're deploying to, if you're deploying a new or modified contract, you'll need to verify it.
-
-## Verifying the Smart Contract
-
-In web3, it's considered best practice to verify your contracts so that users and other developers can inspect the source code, and be sure that it matches the deployed bytecode on the blockchain.
-
-Further, if you want to allow others to interact with your contract using the block explorer, it first needs to be verified. The above contract has already been verified, so you should be able to view your version on a block explorer already, but we'll still walk through how to verify a contract on Base Sepolia testnet.
-
-
-Remember, you need an API key from BaseScan to verify your contracts. You can get your API key from [the BaseScan site](https://basescan.org/myapikey) after you sign up for an account.
-
-
-Grab the deployed address and run:
-
-```bash
-forge verify-contract ./src/NFT.sol:NFT --chain 84532 --watch
-```
-
-You should see an output similar to:
-
-```
-Start verifying contract `0x71bfCe1172A66c1c25A50b49156FAe45EB56E009` deployed on base-sepolia
-
-Submitting verification for [src/NFT.sol:NFT] 0x71bfCe1172A66c1c25A50b49156FAe45EB56E009.
-Submitted contract for verification:
- Response: `OK`
- GUID: `3i9rmtmtyyzkqpfvy7pcxj1wtgqyuybvscnq8d7ywfuskss1s7`
- URL:
- https://sepolia.basescan.org/address/0x71bfce1172a66c1c25a50b49156fae45eb56e009
-Contract verification status:
-Response: `NOTOK`
-Details: `Pending in queue`
-Contract verification status:
-Response: `OK`
-Details: `Pass - Verified`
-Contract successfully verified
-```
-
-Search for your contract on [BaseScan](https://sepolia.basescan.org/) to confirm it is verified.
-
-
-You can't re-verify a contract identical to one that has already been verified. If you attempt to do so, such as verifying the above contract, you'll get an error similar to:
-
-
-```text
-Start verifying contract `0x71bfCe1172A66c1c25A50b49156FAe45EB56E009` deployed on base-sepolia
-
-Contract [src/NFT.sol:NFT] "0x71bfCe1172A66c1c25A50b49156FAe45EB56E009" is already verified. Skipping verification.
-```
-
-## Interacting with the Smart Contract
-
-If you verified on BaseScan, you can use the `Read Contract` and `Write Contract` sections under the `Contract` tab to interact with the deployed contract. To use `Write Contract`, you'll need to connect your wallet first, by clicking the `Connect to Web3` button (sometimes this can be a little finicky, and you'll need to click `Connect` twice before it shows your wallet is successfully connected).
-
-To practice using the `cast` command-line tool which Foundry provides, you'll perform a call without publishing a transaction (a read), then sign and publish a transaction (a write).
-
-### Performing a call
-
-A key component of the Foundry toolkit, `cast` enables us to interact with contracts, send transactions, and get onchain data using Ethereum RPC calls. First you will perform a call from your account, without publishing a transaction.
-
-From the command-line, run:
-
-```bash
-cast call --rpc-url $BASE_SEPOLIA_RPC "balanceOf(address)"
-```
-
-You should receive `0x0000000000000000000000000000000000000000000000000000000000000000` in response, which equals `0` in hexadecimal. And that makes sense — while you've deployed the NFT contract, no NFTs have been minted yet and therefore your account's balance is zero.
-
-### Signing and publishing a transaction
-
-Now, sign and publish a transaction, calling the `mint(address)` function on the NFT contract you just deployed.
-
-Run the following command:
-
-```bash
-cast send --rpc-url=$BASE_SEPOLIA_RPC "mint(address)" --account deployer
-```
-
-
-Note that in this `cast send` command, you had to include your private key, but this is not required for `cast call`, because that's for calling view-only contract functions and therefore you don't need to sign anything.
-
-
-If successful, Foundry will respond with information about the transaction, including the `blockNumber`, `gasUsed`, and `transactionHash`.
-
-Finally, let's confirm that you did indeed mint yourself one NFT. If you run the first `cast call` command again, you should see that your balance increased from 0 to 1:
-
-```bash
-cast call --rpc-url $BASE_SEPOLIA_RPC "balanceOf(address)"
-```
-
-And the response: `0x0000000000000000000000000000000000000000000000000000000000000001` (`1` in hex) — congratulations, you deployed a contract and minted an NFT with Foundry!
-
-## Conclusion
-
-Phew, that was a lot! You learned how to setup a project, deploy to Base, and interact with our smart contract using Foundry. The process is the same for real networks, just more expensive — and of course, you'll want to invest time and effort testing your contracts, to reduce the likelihood of user-impacting bugs before deploying.
-
-For all things Foundry, check out the [Foundry book](https://book.getfoundry.sh/), or head to the official Telegram [dev chat](https://t.me/foundry_rs) or [support chat](https://t.me/foundry_support).
-
-{/*-- Add reference style links here. These do not render on the page. --*/}
-
-[`sepolia.basescan.org`]: https://sepolia.basescan.org/
-[`basescan.org`]: https://basescan.org/
-[coinbase]: https://www.coinbase.com/wallet
-[MetaMask]: https://metamask.io/
-[coinbase settings]: https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings
-[etherscan]: https://etherscan.io/
-[faucets on the web]: https://coinbase.com/faucets
-[Foundry]: https://book.getfoundry.sh/
-
diff --git a/docs/learn/foundry/generate-random-numbers-contracts.md b/docs/learn/foundry/generate-random-numbers-contracts.md
deleted file mode 100644
index 85263849e..000000000
--- a/docs/learn/foundry/generate-random-numbers-contracts.md
+++ /dev/null
@@ -1,403 +0,0 @@
----
-title: Generating random numbers contracts using Supra dVRF
-slug: /oracles-supra-vrf
-description: A tutorial that teaches how to use Supra dVRF to serve random numbers using an onchain randomness generation mechanism directly within your smart contracts on the Base testnet.
-author: taycaldwell
-keywords: [
- Oracle
- Oracles,
- Supra,
- Supra VRF,
- Supra dVRF,
- VRF,
- verifiable random function,
- verifiable random functions,
- random numbers,
- rng,
- random number generator,
- random numbers in smart contracts,
- random numbers on Base,
- smart contract,
- Base blockchain,
- Base network,
- Base testnet,
- Base test network,
- app development,
- dapp development,
- build a dapp on Base,
- build on Base,
- ]
-tags: ['oracles', 'vrf']
-difficulty: intermediate
-displayed_sidebar: null
----
-
-This tutorial will guide you through the process of creating a smart contract on Base that utilizes Supra dVRF to serve random numbers using an onchain randomness generation mechanism directly within your smart contracts.
-
-
-
-## Objectives
-
-By the end of this tutorial you should be able to do the following:
-
-- Set up a smart contract project for Base using Foundry
-- Install the Supra dVRF as a dependency
-- Use Supra dVRF within your smart contract
-- Deploy and test your smart contracts on Base
-
-
-
-## Prerequisites
-
-### Foundry
-
-This tutorial requires you to have Foundry installed.
-
-- From the command-line (terminal), run: `curl -L https://foundry.paradigm.xyz | bash`
-- Then run `foundryup`, to install the latest (nightly) build of Foundry
-
-For more information, see the Foundry Book [installation guide](https://book.getfoundry.sh/getting-started/installation).
-
-### Coinbase Wallet
-
-In order to deploy a smart contract, you will first need a wallet. You can create a wallet by downloading the Coinbase Wallet browser extension.
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-Deploying contracts to the blockchain requires a gas fee. Therefore, you will need to fund your wallet with ETH to cover those gas fees.
-
-For this tutorial, you will be deploying a contract to the Base Sepolia test network. You can fund your wallet with Base Sepolia ETH using one of the faucets listed on the Base [Network Faucets](https://docs.base.org/chain/network-faucets) page.
-
-### Supra wallet registration
-
-
-Supra dVRF V2 requires subscription to the service with a customer controlled wallet address to act as the main reference.
-
-Therefore you must register your wallet with the Supra team if you plan to consume Supra dVRF V2 within your smart contracts.
-
-Please refer to the [Supra documentation](https://docs.supra.com/oracles/dvrf/vrf-subscription-model) for the latest steps on how to register your wallet for their service.
-
-
-
-
-## What is a Verifiable Random Function (VRF)?
-
-A Verifiable Random Function (VRF) provides a solution for generating random outcomes in a manner that is both decentralized and verifiably recorded onchain. VRFs are crucial for applications where randomness integrity is paramount, such as in gaming or prize drawings.
-
-Supra dVRF provides a decentralized VRF that ensures that the outcomes are not only effectively random but also responsive, scalable, and easily verifiable, thereby addressing the unique needs of onchain applications for trustworthy and transparent randomness.
-
-
-
-## Creating a project
-
-Before you can begin writing smart contracts for Base, you need to set up your development environment by creating a Foundry project.
-
-To create a new Foundry project, first create a new directory:
-
-```bash
-mkdir myproject
-```
-
-Then run:
-
-```bash
-cd myproject
-forge init
-```
-
-This will create a Foundry project, which has the following basic layout:
-
-```bash
-.
-├── foundry.toml
-├── script
- │ └── Counter.s.sol
-├── src
- │ └── Counter.sol
-└── test
- └── Counter.t.sol
-```
-
-
-
-## Writing the Smart Contract
-
-Once your Foundry project has been created, you can now start writing a smart contract.
-
-The Solidity code below defines a basic contract named `RNGContract`. The smart contract's constructor takes in a single `address` and assigns it to a member variable named `supraAddr`. This address corresponds to the [contract address](https://docs.supra.com/oracles/data-feeds/pull-oracle/networks) of the Supra Router Contract that will be used to generate random numbers. The contract address of the Supra Router Contract on Base Sepolia testnet is `0x99a021029EBC90020B193e111Ae2726264a111A2`.
-
-The contract also assigns the contract deployer (`msg.sender`) to a member variable named `supraClientAddress`. This should be the client wallet address that is registered and whitelisted to use Supra VRF (see: [Prerequisites](#prerequisites)).
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-contract RNGContract {
- address supraAddr;
- address supraClientAddress;
-
- constructor(address supraSC) {
- supraAddr = supraSC;
- supraClientAddress = msg.sender;
- }
-}
-```
-
-In your project, add the code provided above to a new file named `src/ExampleContract.sol`, and delete the `src/Counter.sol` contract that was generated with the project. (you can also delete the `test/Counter.t.sol` and `script/Counter.s.sol` files).
-
-The following sections will guide you step-by-step on how to update your contract to generate random numbers using the Supra Router contract.
-
-### Adding the Supra Router Contract interface
-
-In order to help your contract (the requester contract) interact with the Supra Router contract and understand what methods it can call, you will need to add the following interface to your contract file.
-
-```solidity
-interface ISupraRouter {
- function generateRequest(string memory _functionSig, uint8 _rngCount, uint256 _numConfirmations, uint256 _clientSeed, address _clientWalletAddress) external returns(uint256);
- function generateRequest(string memory _functionSig, uint8 _rngCount, uint256 _numConfirmations, address _clientWalletAddress) external returns(uint256);
-}
-```
-
-The `ISupraRouter` interface defines a `generateRequest` function. This function is used to create a request for random numbers. The `generateRequest` function is defined twice, because one of the definitions allows for an optional `_clientSeed` (defaults to `0`) for additional unpredictability.
-
-
-Alternatively, you can add the `ISupraRouter` interface in a separate interface file and inherit the interface in your contract.
-
-
-### Adding a request function
-
-Once you have defined the `ISupraRouter`, you are ready to add the logic to your smart contract for requesting random numbers.
-
-For Supra dVRF, adding logic for requesting random numbers requires two functions:
-
-- A request function
-- A callback function
-
-The request function is a custom function defined by the developer. There are no requirements when it comes to the signature of the request function.
-
-The following code is an example of a request function named `rng` that requests random numbers using the Supra Router Contract. Add this function to your smart contract:
-
-```solidity
-function rng() external returns (uint256) {
- // Amount of random numbers to request
- uint8 rngCount = 5;
- // Amount of confirmations before the request is considered complete/final
- uint256 numConfirmations = 1;
- uint256 nonce = ISupraRouter(supraAddr).generateRequest(
- "requestCallback(uint256,uint256[])",
- rngCount,
- numConfirmations,
- supraClientAddress
- );
- return nonce;
- // store nonce if necessary (e.g., in a hashmap)
- // this can be used to track parameters related to the request in a lookup table
- // these can be accessed inside the callback since the response from supra will include the nonce
-}
-```
-
-The `rng` function above requests `5` random numbers (defined by `rngCount`), and waits `1` confirmation (defined by `numConfirmations`) before considering the result to be final. It makes this request by calling the `generateRequest` function of the Supra Router contract, while providing a callback function with the signature `requestCallback(uint256,uint256[])`.
-
-### Adding a callback function
-
-As seen in the previous section, the `generateRequest` method of the Supra Router contract expects a signature for a callback function. This callback function must be of the form: `uint256 nonce, uint256[] calldata rngList`, and must include validation code, such that only the Supra Router contract can call the function.
-
-To do this, add the following callback function (`requestCallback`) to your smart contract:
-
-```solidity
-function requestCallback(uint256 _nonce ,uint256[] _rngList) external {
- require(msg.sender == supraAddr, "Only the Supra Router can call this function.");
- uint8 i = 0;
- uint256[] memory x = new uint256[](rngList.length);
- rngForNonce[nonce] = x;
- for(i=0; i uint256[] ) rngForNonce;
-```
-
-### Adding a function to view the result
-
-To fetch resulting random numbers based on their associated `nonce`, you add a third function:
-
-```solidity
-function viewRngForNonce(uint256 nonce) external view returns (uint256[] memory) {
- return rngForNonce[nonce];
-}
-```
-
-### Final smart contract code
-
-After following all the steps above, your smart contract code should look like the following:
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-interface ISupraRouter {
- function generateRequest(string memory _functionSig, uint8 _rngCount, uint256 _numConfirmations, uint256 _clientSeed, address _clientWalletAddress) external returns (uint256);
- function generateRequest(string memory _functionSig, uint8 _rngCount, uint256 _numConfirmations, address _clientWalletAddress) external returns (uint256);
-}
-
-contract RNGContract {
- address supraAddr;
- address supraClientAddress;
-
- mapping (uint256 => uint256[]) rngForNonce;
-
- constructor(address supraSC) {
- supraAddr = supraSC;
- supraClientAddress = msg.sender;
- }
-
- function rng() external returns (uint256) {
- // Amount of random numbers to request
- uint8 rngCount = 5;
- // Amount of confirmations before the request is considered complete/final
- uint256 numConfirmations = 1;
- uint256 nonce = ISupraRouter(supraAddr).generateRequest(
- "requestCallback(uint256,uint256[])",
- rngCount,
- numConfirmations,
- supraClientAddress
- );
- return nonce;
- }
-
- function requestCallback(uint256 _nonce, uint256[] memory _rngList) external {
- require(msg.sender == supraAddr, "Only the Supra Router can call this function.");
- uint8 i = 0;
- uint256[] memory x = new uint256[](_rngList.length);
- rngForNonce[_nonce] = x;
- for (i = 0; i < _rngList.length; i++) {
- rngForNonce[_nonce][i] = _rngList[i] % 100;
- }
- }
-
- function viewRngForNonce(uint256 nonce) external view returns (uint256[] memory) {
- return rngForNonce[nonce];
- }
-}
-```
-
-
-You must whitelist this smart contract under the wallet address you registered with Supra, and deposit funds to be paid for the gas fees associated with transactions for your callback function.
-
-Follow the [guidance steps](https://supraoracles.com/docs/vrf/v2-guide#step-1-create-the-supra-router-contract-interface-1) provided by Supra for whitelisting your contract and depositing funds.
-
-If you have not yet registered your wallet with Supra, see the [Prerequisites](#prerequisites) section.
-
-
-
-
-## Compiling the Smart Contract
-
-To compile your smart contract code, run:
-
-```bash
-forge build
-```
-
-
-
-## Deploying the smart contract
-
-### Setting up your wallet as the deployer
-
-Before you can deploy your smart contract to the Base network, you will need to set up a wallet to be used as the deployer.
-
-To do so, you can use the [`cast wallet import`](https://book.getfoundry.sh/reference/cast/cast-wallet-import) command to import the private key of the wallet into Foundry's securely encrypted keystore:
-
-```bash
-cast wallet import deployer --interactive
-```
-
-After running the command above, you will be prompted to enter your private key, as well as a password for signing transactions.
-
-
-For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key).
-
-**It is critical that you do NOT commit this to a public repo**.
-
-
-To confirm that the wallet was imported as the `deployer` account in your Foundry project, run:
-
-```bash
-cast wallet list
-```
-
-### Setting up environment variables for Base Sepolia
-
-To setup your environment for deploying to the Base network, create an `.env` file in the home directory of your project, and add the RPC URL for the Base Sepolia testnet, as well as the Supra Router contract address for Base Sepolia testnet:
-
-```
-BASE_SEPOLIA_RPC="https://sepolia.base.org"
-ISUPRA_ROUTER_ADDRESS=0x99a021029EBC90020B193e111Ae2726264a111A2
-```
-
-Once the `.env` file has been created, run the following command to load the environment variables in the current command line session:
-
-```bash
-source .env
-```
-
-### Deploying the smart contract to Base Sepolia
-
-With your contract compiled and environment setup, you are ready to deploy the smart contract to the Base Sepolia Testnet!
-
-For deploying a single smart contract using Foundry, you can use the `forge create` command. The command requires you to specify the smart contract you want to deploy, an RPC URL of the network you want to deploy to, and the account you want to deploy with.
-
-To deploy the `RNGContract` smart contract to the Base Sepolia test network, run the following command:
-
-```bash
-forge create ./src/RNGContract.sol:RNGContract --rpc-url $BASE_SEPOLIA_RPC --constructor-args $ISUPRA_ROUTER_ADDRESS --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-
-Your wallet must be funded with ETH on the Base Sepolia Testnet to cover the gas fees associated with the smart contract deployment. Otherwise, the deployment will fail.
-
-To get testnet ETH for Base Sepolia, see the [prerequisites](#prerequisites).
-
-
-After running the command above, the contract will be deployed on the Base Sepolia test network. You can view the deployment status and contract by using a [block explorer](/chain/block-explorers).
-
-
-
-## Interacting with the Smart Contract
-
-Foundry provides the `cast` command-line tool that can be used to interact with the smart contract that was deployed and call the `getLatestPrice()` function to fetch the latest price of ETH.
-
-To call the `getLatestPrice()` function of the smart contract, run:
-
-```bash
-cast call --rpc-url $BASE_SEPOLIA_RPC "rng()"
-```
-
-You should receive a `nonce` value.
-
-You can use this `nonce` value to call the `viewRngForNonce(uint256)` function to get the resulting list of randomly generated numbers:
-
-```bash
-cast call --rpc-url $BASE_SEPOLIA_RPC "viewRngForNonce(uint256)"
-```
-
-
-
-## Conclusion
-
-Congratulations! You have successfully deployed and interacted with a smart contract that generates a list of random numbers using Supra dVRF on the Base blockchain network.
-
-To learn more about VRF and using Supra dVRF to generate random numbers within your smart contracts on Base, check out the following resources:
-
-- [Oracles](https://docs.base.org/tools/oracles)
-- [Supra dVRF - Developer Guide V2](https://supraoracles.com/docs/vrf/v2-guide)
diff --git a/docs/learn/foundry/setup-with-base.mdx b/docs/learn/foundry/setup-with-base.mdx
deleted file mode 100644
index 9400d86ff..000000000
--- a/docs/learn/foundry/setup-with-base.mdx
+++ /dev/null
@@ -1,148 +0,0 @@
----
-title: 'Foundry: Setting up Foundry with Base'
-slug: /intro-to-foundry-setup
-description: A tutorial that teaches how to set up your development environment to work with Foundry.
-author: Edson Alcala
----
-
-# Setting up Foundry with Base
-
-In this tutorial, you'll learn how to set up [Foundry], a toolchain for smart contract development. You'll also learn how to configure it to work with Base.
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-- Install Foundry
-- Create a Foundry project
-- Compile a smart contract using Foundry
-- Configure Foundry to work with Base
-
-## Overview
-
-Foundry is a smart contract development toolchain that is composed of multiple small command line tools:
-
-- _[forge]_: Compile, test, and deploy your smart contracts
-- _[cast]_: Interact with the Blockchain over RPC. You can make smart contract calls, send transactions, or retrieve any type of chain data
-- _[chisel]_: A Solidity REPL. You can write Solidity code directly
-- _[anvil]_: A local Blockchain node for testing and development
-
-Using Foundry you can manage your dependencies, compile your project, run tests, deploy smart contracts and interact with the chain from the command-line and via Solidity scripts.
-
-For a deep dive on the Foundry features and full capabilities, check out the [Foundry Book].
-
-## Installing Foundry
-
-In order to install Foundry, you can use `Foundryup`, the Foundry's toolchain installer.
-
-To install `Foundryup` you have to run in the terminal:
-
-```bash
-$ curl -L https://foundry.paradigm.xyz | bash
-```
-
-After `Foundryup` is installed, you can install `Foundry` by running:
-
-```bash
-$ foundryup
-```
-
-You can verify the installation by trying the following commands:
-
-```bash
-$ forge --version
-$ cast --version
-$ chisel --version
-$ anvil --version
-```
-
-## My First Foundry Project
-
-To create a foundry project you can simply run:
-
-```bash filename="terminal"
-$ forge init hello_foundry_in_base
-```
-
-This will create a foundry project with the following structure:
-
-```text filename="project-structure"
-├── lib # all the libraries installed
-├── script # scripts folder, e.g., deploy scripts
-├── src # smart contracts folder
-├── test # tests folder
-└── foundry.toml # foundry configuration file
-```
-
-You will also notice a `.gitmodules` file -- this is because `Foundry` handles dependencies using [Git submodules].
-
-By default the Foundry structure stores smart contracts in the `src` folder. You can change this in the `foundry.toml` configuration file.
-
-For instance:
-
-```toml filename="foundry.toml"
-[profile.default]
-src = 'contracts'
-```
-
-In order to compile the project, simply run:
-
-```bash filename="terminal"
-forge build
-```
-
-## Setting up Foundry with Base
-
-In order to work with Base, you need to configure a couple of settings in the configuration `foundry.toml` file.
-
-The first thing is the Solidity version.
-
-You need to configure your config file as follows:
-
-```toml filename="foundry.toml"
-[profile.default]
-src = 'src'
-out = 'out'
-libs = ['lib']
-solc_version = "0.8.23"
-```
-
-Be sure that you modify the pragma of your contracts and simply run `forge build` to ensure everything works well.
-
-We also recommend setting up JSON RPC endpoints for Base and the API key for [Basescan] in the configuration file so that your environment is ready to deploy your smart contracts.
-
-Your configuration file should look like the following:
-
-```toml filename="foundry.toml"
-[profile.default]
-src = "src"
-out = "out"
-libs = ["lib"]
-solc_version = "0.8.23"
-
-[rpc_endpoints]
-base = "https://mainnet.base.org"
-baseSepolia = "https://sepolia.base.org"
-
-[etherscan]
-baseSepolia = { key = "${BASESCAN_API_KEY}", url = "https://api-sepolia.basescan.org/api" }
-base = { key = "${BASESCAN_API_KEY}", url = "https://api.basescan.org/api" }
-```
-
-We included 2 JSON RPC endpoints for `Base` and `Base Sepolia` and similar for the Etherscan section, we included the configuration for `Basescan` for Sepolia and Mainnet. Both rely on the same API Key, `BASESCAN_API_KEY`.
-
-## Conclusion
-
-In this tutorial, you've embarked on the journey of smart contract development with Base and Foundry. You've learned the essential steps, from installing Foundry using the convenient `Foundryup` toolchain installer to creating your first project and configuring Foundry to seamlessly integrate with Base.
-
-[Foundry]: https://github.com/foundry-rs/foundry
-[Foundry Book]: https://book.getfoundry.sh/
-[chisel]: https://book.getfoundry.sh/chisel/
-[cast]: https://book.getfoundry.sh/cast/
-[anvil]: https://book.getfoundry.sh/anvil/
-[forge]: https://book.getfoundry.sh/forge/
-[Git submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
-[OP Stack]: https://docs.optimism.io/
-[Differences between Ethereum and Base]: https://docs.base.org/differences/
-[Basescan]: https://basescan.org/
-
diff --git a/docs/learn/foundry/testing-smart-contracts.mdx b/docs/learn/foundry/testing-smart-contracts.mdx
deleted file mode 100644
index 4208e2158..000000000
--- a/docs/learn/foundry/testing-smart-contracts.mdx
+++ /dev/null
@@ -1,215 +0,0 @@
----
-title: 'Foundry: Testing smart contracts'
-slug: /intro-to-foundry-testing
-author: Edson Alcala
-description: A tutorial that teaches how to test your smart contracts using Foundry.
----
-
-# Testing smart contracts using Foundry
-
-In this tutorial, you'll learn how to test your smart contracts using [Foundry], the toolchain for smart contract development.
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-- Understand the increased importance of testing in smart contract development
-- Write and execute tests written in Solidity using the Forge Standard Library with Foundry
-- Use the `cheatcodes` that Foundry provides to test your smart contracts
-
-## Overview
-
-Testing is a crucial aspect of smart contract development, ensuring the reliability and security of your code. Because it is impossible to patch a smart contract after deployment, you must thoroughly and completely test your code. Foundry provides a robust testing framework that allows developers to create comprehensive test suites for their projects using Solidity.
-
-## My First Test with Foundry
-
-Consider the default test that the `forge init hello_foundry_in_base` command provides in the seed Foundry project.
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "forge-std/Test.sol";
-import "../src/Counter.sol";
-
-contract CounterTest is Test {
- Counter public counter;
-
- function setUp() public {
- counter = new Counter();
- counter.setNumber(0);
- }
-
- function testIncrement() public {
- counter.increment();
- assertEq(counter.number(), 1);
- }
-
- function testSetNumber(uint256 x) public {
- counter.setNumber(x);
- assertEq(counter.number(), x);
- }
-}
-```
-
-Take note of the following:
-
-- Foundry test files are named following the pattern: `.t.sol`
-- Smart contract test files are named following the pattern: `Test`
-- All tests inherit from `forge-std/Test.sol`.
-- All tests contain a public function called `setUp`, which is executed before each test. This is similar to the `beforeEach` hook in the Mocha/Typescript world.
-- Test cases start with the `test` keyword, for instance `testIncrement`.
-- Test cases functions are public.
-
-For more information about writing tests in Foundry, you can follow the official guide for [Writing tests]
-
-In order to run the test in Foundry, run:
-
-```bash
-$ forge test
-```
-
-You should see in the terminal:
-
-```bash
-Running 2 tests for test/Counter.t.sol:CounterTest
-[PASS] testIncrement() (gas: 28334)
-[PASS] testSetNumber(uint256) (runs: 256, μ: 27565, ~: 28343)
-Test result: ok. 2 passed; 0 failed; finished in 13.57ms
-```
-
-## Using Cheatcodes
-
-Foundry includes a set of [cheatcodes], which are special instructions that are accessible using the `vm` instance in your tests. Cheatcodes allow you to perform various tasks, including:
-
-- Manipulate the state of the blockchain
-- Test reverts
-- Test events
-- Change block number
-- Change identity
-- And more!
-
-To start, use a cheatcode to modify the `msg.sender` of your tests, and add some console logs via importing the `forge-std/console.sol` contract.
-
-The `Counter` contract should look as follows:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "forge-std/console.sol";
-
-contract Counter {
- uint256 public number;
-
- function setNumber(uint256 newNumber) public {
- console.log("The sender is %s", msg.sender);
- number = newNumber;
- }
-
- function increment() public {
- console.log("The sender is %s", msg.sender);
- number++;
- }
-}
-```
-
-If you run the tests using `forge test`, you will see the following:
-
-```bash
-Running 2 tests for test/Counter.t.sol:CounterTest
-[PASS] testIncrement() (gas: 31531)
-[PASS] testSetNumber(uint256) (runs: 256, μ: 30684, ~: 31540)
-Test result: ok. 2 passed; 0 failed; finished in 19.64ms
-```
-
-It seems the logs are not being shown. The reason is because the `forge test` command includes a flag that enable you to include more details of the logs emitted during the execution of the tests.
-
-You can control that by including different levels of the verbose flag -- `-vv` up to `-vvvvv`. For more details about the level of verbosity you can refer to the [Logs and Traces] section of the Foundry documentation.
-
-Run the `foundry test -vv`. You should see:
-
-```bash
-Running 2 tests for test/Counter.t.sol:CounterTest
-[PASS] testIncrement() (gas: 31531)
-Logs:
- The sender is 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496
- The sender is 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496
-
-[PASS] testSetNumber(uint256) (runs: 256, μ: 30607, ~: 31540)
-Logs:
- The sender is 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496
-
-Test result: ok. 2 passed; 0 failed; finished in 17.89ms
-```
-
-Now, modify the test file using `prank` cheatcode, which allow you to modify the `msg.sender` of the next transaction. You will also use the `addr` cheatcode, which allow you to generate an address using any private key, which can simply be a hex number.
-
-Include some `console.log` statements to understand better the execution flow.
-
-The code should look like:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "forge-std/Test.sol";
-import "../src/Counter.sol";
-
-contract CounterTest is Test {
- Counter public counter;
-
- function setUp() public {
- counter = new Counter();
- console.log("Calling on Setup");
- counter.setNumber(0);
- }
-
- function testIncrement() public {
- console.log("Calling on testIncrement");
- vm.prank(vm.addr(0x01));
- counter.increment();
- assertEq(counter.number(), 1);
- }
-
- function testSetNumber(uint256 x) public {
- console.log("Calling on testSetNumber");
- vm.prank(vm.addr(0x02));
- counter.setNumber(x);
- assertEq(counter.number(), x);
- }
-}
-```
-
-Then if you run the `forge test -vv` command, you should see:
-
-```bash
-Running 2 tests for test/Counter.t.sol:CounterTest
-[PASS] testIncrement() (gas: 35500)
-Logs:
- Calling on Setup
- The sender is 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496
- Calling on testIncrement
- The sender is 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
-
-[PASS] testSetNumber(uint256) (runs: 256, μ: 34961, ~: 35506)
-Logs:
- Calling on Setup
- The sender is 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496
-
-Test result: ok. 2 passed; 0 failed; finished in 48.75ms
-```
-
-Notice how you call the cheatcode `vm.prank` before the call to the `counter.increment()` and `counter.setNumber(x)` functions. This allows you to specify a particular address to become the `msg.sender` in the contract. Since the `vm.prank` accepts an address, you simply generate an address using the cheatcode `vm.addr`, where you pass a simple hexadecimal number, which is a valid private key.
-
-## Conclusion
-
-Congratulations! You've successfully completed your first step in your journey of testing smart contracts using Foundry. As you move forward, keep exploring its rich features and functionalities. The ability to write comprehensive tests and leverage cheatcodes ensures the reliability and security of your smart contracts.
-
-Happy coding and testing with Foundry!
-
-[Foundry]: https://book.getfoundry.sh/
-[Writing tests]: https://book.getfoundry.sh/forge/writing-tests
-[cheatcodes]: https://book.getfoundry.sh/forge/cheatcodes
-[Logs and Traces]: https://book.getfoundry.sh/forge/tests?highlight=vvv#logs-and-traces
-
diff --git a/docs/learn/foundry/verify-contract-with-basescan.md b/docs/learn/foundry/verify-contract-with-basescan.md
deleted file mode 100644
index 1fbe9da20..000000000
--- a/docs/learn/foundry/verify-contract-with-basescan.md
+++ /dev/null
@@ -1,289 +0,0 @@
----
-title: 'Verify a Smart Contract using Basescan API'
-slug: /verify-smart-contract-using-basescan
-description: A tutorial that teaches how to verify a smart contract using Basescan APIs.
-author: hughescoin
----
-
-[Basescan] is a block explorer specifically designed for [Base], offering developers a way to interact with and verify the smart contracts deployed on Base. Smart contract verification is a critical step in ensuring the transparency and security of onchain applications, as it allows others to review and validate the source code of deployed contracts. There are multiple ways to verify contracts and by the end of this tutorial you will learn how to verify a contract using the [Solidity] single file verification method using the [Basescan API].
-
-
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-- Deploy a smart contract using [Foundry]
-- Interact with the [Basescan API] to verify your deployed contract
-- Obtain and configure a (free) Base RPC Node from [Coinbase Developer Platform (CDP)](https://portal.cdp.coinbase.com/products/node)
-
-
-
-## Prerequisites
-
-**Familiarity with smart contract development and the Solidity programming language**
-
-Solidity is the primary programming language for writing smart contracts on Ethereum and Ethereum-compatible blockchains like Base. You should be comfortable with writing, compiling, and deploying basic smart contracts using Solidity. If not, check out [Base Learn].
-
-**Basic understanding of Foundry for Ethereum development**
-
-Foundry is a fast and portable toolkit for Ethereum application development. It simplifies the process of deploying, testing, and interacting with smart contracts. This tutorial assumes you have experience using Foundry to compile and [deploy smart contracts].
-
-**Access to a Coinbase Developer Platform (CDP) account**
-
-The [Coinbase Developer Platform] provides access to tools and services necessary for blockchain development, including RPC nodes for different networks. You'll need to sign up for a CDP account to obtain a [Base RPC Node], which will be essential for deploying and interacting with your smart contracts on the Base blockchain.
-
-**Node + Basic API requests**
-
-## Jump Right In
-
-For this tutorial, you will deploy a simple contract that is included in the Foundry quickstart. To do so, ensure that you have Foundry installed.
-
-If you don't have Foundry install it:
-
-```bash
-curl -L https://foundry.paradigm.xyz | bash
-```
-
-Once installed, create a Foundry project:
-
-```bash
-forge init verify_contracts
-```
-
-then change into the newly made directory:
-
-```bash
-cd verify_contracts
-```
-
-You should have a folder structure similar to this:
-
-```bash
-├── lib
-├── script
-├── src
-└── test
-```
-
-The `src` folder will contain a `Counter.sol` file which will serve as the contract you want to deploy.
-
-
-You (the deployer wallet) will need some ETH in order to broadcast the transaction to the Base network. Fortunately, transactions are usually < 1 cent on Base mainnet.
-
-If using a [Coinbase Wallet] use the "Buy" button to onramp crypto from your Coinbase account.
-
-
-You will need a private key of the wallet that you want to deploy the smart contract from. Obtain it and store it as an env variable in your terminal.
-
-Once you have the private key to the wallet of your choice, open your terminal and store it in an environment variable:
-
-```bash
-export PRIVATE_KEY=""
-```
-
-To deploy our contract you will need an RPC URL to a Base node in order to broadcast our transactions to the network. [CDP] provides us with a free node for interacting with Base mainnet and testnet.
-
-Obtain an rpc url from the [Node product] and store the url as an environment variable similar to the private key in the previous step.
-
-
-
-Then store it as an environment variable in your terminal:
-
-```bash
-export BASE_RPC_URL="your_base_rpc_url"
-```
-
-It's deployment time! Deploy the `Counter.sol` contract using `forge create --rpc-url $BASE_RPC_URL --private-key $PRIVATE_KEY src/Counter.sol:Counter`
-
-Once deployed, it should return something like this:
-
-```bash ⠊ Compiling...
-[⠢] Compiling 1 files with Solc 0.8.26
-[⠆] Solc 0.8.26 finished in 1.23s
-Compiler run successful!
-Deployer: 0xLo69e5523D33FBDbF133E81C91639e9d3C6cb369
-Deployed to: 0xEF5fe818Cb814E5c8277C5F12B57106B4EC3DdaA
-Transaction hash: 0xb191f9679a1fee253cf430ac09a6838f6806cfb2a250757fef407880f5546836
-```
-
-Congrats! You've now deployed a contract to Base. The output of the deployment command contains a contract address (e.g `Deployed to: 0xEF5fe818Cb814E5c8277C5F12B57106B4EC3DdaA`). Copy this address as you will need it in the next step.
-
-### Verify the contract
-
-You will now interact with the [Basescan API]. For this, you need API Keys. Create an account using an email or [login to Basescan].
-
-After signing in, navigate to your [Basescan account] then select `API Keys` on the left navigation bar.
-
-
-
-From the [API Key page], click the blue "Add" button to create a new API Key then copy your `API Key Token`
-
-
-
-Save this to your clipboard for the next step.
-
-Create a `.js` file to create a function to that will call the Basescan's contract verification endpoint.
-
-In your terminal create a new file: `touch verifyContractBasescan.js` then open this file in your IDE of choice.
-
-At the top of the file create a variable that contains the `Counter.sol` that was created from your foundry project.
-
-```javascript
-const sourceCode = `pragma solidity ^0.8.13;
-contract Counter {
- uint256 public number;
- function setNumber(uint256 newNumber) public {
- number = newNumber;
-}
- function increment() public {
- number++;
- }
-}`;
-```
-
-Create an `async` function to call the basescan api. Basescan offers a few endpoints to interact with their api with the base url being: `https://api.basescan.org/api`
-
-To verify a contract you will use the `verifysourcecode` route, with the `contract` module, and your `apiKey` as query parameters.
-
-
-In every foundry project you will have a `.json` file that contains the contracts metadata and ABI. For this particular project, this information is located in the `/verify_contracts/out/Counter.sol/Counter.json`
-
-Under the `Metadata` object you will find the compiler version under `evmversion`
-
-
-Putting everything together, our function will look like this:
-
-```
-async function verifySourceCode() {
- const url = 'https://api.basescan.org/api';
-
- const params = new URLSearchParams({
- module: 'contract',
- action: 'verifysourcecode',
- apikey: 'DK8M329VYXDSKTF633ABTK3SAEZ2U9P8FK', //remove hardcode
- });
-
- const data = new URLSearchParams({
- chainId: '8453',
- codeformat: 'solidity-single-file',
- sourceCode: sourceCode,
- contractaddress: '0x8aB096ea9886ACe363f81068d2439033F67F62E4',
- contractname: 'Counter',
- compilerversion: 'v0.8.26+commit.8a97fa7a',
- optimizationUsed: 0,
- evmversion: 'paris',
- });
- }
-```
-
-Now add a `try-catch` block to send the request and log any errors to the console.
-
-Your final file should look something like this:
-
-```javascript
-const sourceCode = `pragma solidity ^0.8.13;
-
-contract Counter {
- uint256 public number;
-
- function setNumber(uint256 newNumber) public {
- number = newNumber;
- }
-
- function increment() public {
- number++;
- }
-}`;
-
-async function verifySourceCode() {
- const url = 'https://api.basescan.org/api';
-
- const params = new URLSearchParams({
- module: 'contract',
- action: 'verifysourcecode',
- apikey: 'YOUR_API_KEY',
- });
-
- const data = new URLSearchParams({
- chainId: '8453',
- codeformat: 'solidity-single-file',
- sourceCode: sourceCode,
- contractaddress: '0x8aB096ea9886ACe363f81068d2439033F67F62E4',
- contractname: 'Counter',
- compilerversion: 'v0.8.26+commit.8a97fa7a',
- optimizationUsed: 0,
- evmversion: 'paris',
- });
-
- try {
- const response = await fetch(`${url}?${params}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: data,
- });
-
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
-
- const result = await response.json();
- console.log(result);
- return result;
- } catch (error) {
- console.error('Error:', error);
- throw error;
- }
-}
-
-verifySourceCode().catch((error) => console.error('Unhandled error:', error));
-```
-
-Save your file and then run `node verifyContractBasescan.js` in your terminal
-
-If successful, your terminal will output JSON text with three properties `status`, `message` and `result` like below:
-
-```bash
-{
- status: '1',
- message: 'OK',
- result: 'cqjzzvppgswqw5adq4v6iq4xkmf519pj1higvcxsdiwcvwxemd'
-}
-```
-
-Result is the GUID and is a unique identifier for checking the status of your contracts verification.
-
-To verify the contract, let's create a curl request with the following parameters
-
-```bash
-curl "https://api.basescan.org/api?module=contract&action=checkverifystatus&guid=cqjzzvppgswqw5adq4v6iq4xkmf519pj1higvcxsdiwcvwxemd&apikey=DK8M329VYXDSKTF633ABTK3SAEZ2U9P8FK"
-```
-
-Run the command and you will see a that the contract should already be verified based on the `result` field
-
-```json
-{ "status": "0", "message": "NOTOK", "result": "Already Verified" }
-```
-
-## Conclusion
-
-Congratulations! You've successfully deployed and verified a smart contract using the Basescan API. Now, your users don't have to rely solely on your word—they can verify the contract's functionality through the code itself.
-
-
-
-[Coinbase Developer Platform]: https://portal.cdp.coinbase.com/
-[Base RPC Node]: https://portal.cdp.coinbase.com/products/node
-[CDP]: https://portal.cdp.coinbase.com/
-[Base]: https://base.org/
-[Basescan]: https://basescan.org/
-[Solidity]: https://soliditylang.org/
-[Basescan account]: https://basescan.org/myaccount/
-[API Key page]: https://basescan.org/myapikey/
-[Basescan API]: https://docs.basescan.org/
-[login to Basescan]: https://basescan.org/login/
-[Node product]: https://portal.cdp.coinbase.com/products/node/
-[deploy smart contracts]: https://docs.base.org/tutorials/deploy-with-foundry/
-[Base Learn]: https://docs.base.org/learn/welcome/
-[Foundry]: https://book.getfoundry.sh/getting-started/installation
diff --git a/docs/learn/hardhat/etherscan/etherscan-sbs.mdx b/docs/learn/hardhat/etherscan/etherscan-sbs.mdx
deleted file mode 100644
index 399f2a443..000000000
--- a/docs/learn/hardhat/etherscan/etherscan-sbs.mdx
+++ /dev/null
@@ -1,131 +0,0 @@
----
-title: Etherscan
-description: Learn about Etherscan
-hide_table_of_contents: false
----
-
-# Etherscan
-
-In this article, you'll learn about Etherscan, a blockchain explorer to inspect the Blockchain state and activity.
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- List some of the features of Etherscan
-- Read data from the Bored Ape Yacht Club contract on Etherscan
-- Write data to a contract using Etherscan.
-
----
-
-## Overview
-
-[Etherscan](https://etherscan.io) is a popular Blockchain explorer that works for several different networks. In it, you can explore the state and activity of a particular network.
-
-
-
-
-
-You can explore:
-
-- Blocks
-- Transactions
-- Smart contracts
-- And more!
-
-For instance, the following shows the details of a Block:
-
-
-
-
-
-Where you see information such as:
-
-- Timestamp
-- Transactions
-- Block height
-- And other details
-
-There are many variations of Etherscan for different networks. For example:
-
-- [Base](https://basescan.org)
-- [Base Sepolia](https://sepolia.basescan.org)
-- [Sepolia Etherscan](https://sepolia.etherscan.io)
-
-## Reading data from smart contracts using Etherscan
-
-One of the things you can do with Etherscan is interact with already-deployed contracts.
-
-For example, if you want to read information from a famous contract such as [BAYC](https://etherscan.io/token/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d), you can simply go to Etherscan and explore the contract:
-
-
-
-
-
-You are able to see information such as:
-
-- The ETH balance it holds
-- The contract creator
-- The transaction when it was created
-- Latest transactions
-- And the verified contract
-
-In the **Contract** tab, you can see the full source code of BAYC:
-
-
-
-
-
-For a developer, verifying contracts is important since it gives transparency to your users. However, there are some risks because this means that bad actors can see the full source code and can try to exploit it.
-
-In order to read the state of the BAYC, you can go to the main menu and select the option **Read Contract**:
-
-
-
-
-
-After you select that option, you are able to see all of the read functions of the contract.
-
-You can also query who is the owner of the BAYC with id 150:
-
-
-
-
-
-## Writing data to smart contracts using Etherscan
-
-In a similar fashion, you can read data from smart contracts using Etherscan. It is also possible to write data.
-
-To write data, go to the **Write Contract** tab:
-
-
-
-
-
-From there, connect your wallet by clicking the **Connect with web3** button.
-
-After you connect, the following UI appears:
-
-
-
-
-
-You can then call the functions you wish to write to.
-
-
-Be aware that you may need to have real Ethereum in case you want to write to a contract in Ethereum mainnet. Also, any logic that the smart contract defines will be respected. This means that if you try to write to a contract that verifies certain conditions during the transaction (e.g., a function where only the owner of the contract can write information), then you won't be able to execute the transaction if you are not the owner.
-
-
-## Conclusion
-
-In this lesson, you've learned some of the main features of Etherscan, including interacting with already-deployed contracts such as BAYC in order to read and write data.
-
----
-
-## See also
-
-[Base]: https://basescan.org
-[Base Sepolia]: https://sepolia.basescan.org
-[Sepolia Etherscan]: https://sepolia.etherscan.io
diff --git a/docs/learn/hardhat/etherscan/etherscan-vid.mdx b/docs/learn/hardhat/etherscan/etherscan-vid.mdx
deleted file mode 100644
index b06f7964a..000000000
--- a/docs/learn/hardhat/etherscan/etherscan-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Etherscan
-description: Use Etherscan to interact with your own and others's contracts.
-hide_table_of_contents: false
----
-
-# Etherscan
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/hardhat/hardhat-deploy/deployment-vid.mdx b/docs/learn/hardhat/hardhat-deploy/deployment-vid.mdx
deleted file mode 100644
index 3e3527c50..000000000
--- a/docs/learn/hardhat/hardhat-deploy/deployment-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Deployment
-description: Configure test networks.
-hide_table_of_contents: false
----
-
-# Deployment
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.mdx b/docs/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.mdx
deleted file mode 100644
index 63a82808a..000000000
--- a/docs/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.mdx
+++ /dev/null
@@ -1,263 +0,0 @@
----
-title: Deploying Smart Contracts
-description: Deploy smart contracts with hardhat deploy and hardhat
-hide_table_of_contents: false
----
-
-# Deploying Smart Contracts
-
-In this article, you'll learn how to deploy smart contracts to multiple Blockchain networks using Hardhat and Hardhat deploy.
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- Deploy a smart contract to the Base Sepolia Testnet with hardhat-deploy
-- Deploy a smart contract to the Sepolia Testnet with hardhat-deploy
-- Use BaseScan to view a deployed smart contract
-
----
-
-## Overview
-
-Hardhat capabilities enable developers to deploy smart contracts easily to any Blockchain by simply creating `tasks` or `scripts`. However, due to the Hardhat architecture that enables its extension by creating plugins, you can rely on existing solutions developed by the community.
-
-[Hardhat deploy](https://github.com/wighawag/hardhat-deploy) is a community-developed plugin that enables the deployment of your smart contracts in a simple way.
-
-## Setting up Hardhat deploy
-
-To install:
-
-1. Run `npm install -D hardhat-deploy`. Then, import hardhat-deploy in `hardhat.config.ts`:
-
-```tsx
-import 'hardhat-deploy';
-```
-
-2. Create a folder called deploy and inside it create a new file called `001_deploy_lock.ts`.
-
-3. Include the following:
-
-```tsx
-import { HardhatRuntimeEnvironment } from 'hardhat/types';
-import { DeployFunction } from 'hardhat-deploy/types';
-
-const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
- // code here
-};
-export default func;
-```
-
-4. Modify the `tsconfig.json` file to look like:
-
-```json
-{
- "compilerOptions": {
- "target": "es2020",
- "module": "commonjs",
- "esModuleInterop": true,
- "forceConsistentCasingInFileNames": true,
- "strict": true,
- "skipLibCheck": true,
- "resolveJsonModule": true
- },
- "include": ["./hardhat.config.ts", "./scripts", "./deploy", "./test"]
-}
-```
-
-5. Before implementing the deploy functionality, configure a deployer account in the `hardhat.config.ts` file. Hardhat deployment includes a way to name accounts in the config file.
-
-6. Run the following, which adds an alias to the account 0 of your environment:
-
-```tsx
-const config: HardhatUserConfig = {
- solidity: '0.8.23',
- namedAccounts: {
- deployer: 0,
- },
-};
-```
-
-7. Implement the deploy function by including the following in the `001_deploy_lock.ts` file:
-
-```tsx
-import { HardhatRuntimeEnvironment } from 'hardhat/types';
-import { DeployFunction } from 'hardhat-deploy/types';
-import { ethers } from 'hardhat';
-
-const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
- const { deploy } = hre.deployments;
- // We can now use deployer
- const { deployer } = await hre.getNamedAccounts();
-
- // The value we want to lock
- const VALUE_LOCKED = hre.ethers.parseEther('0.01');
-
- // The unlock time after deployment
- const UNLOCK_TIME = 10000;
-
- // We use ethers to get the current time stamp
- const blockNumber = await ethers.provider.getBlockNumber();
- const lastBlockTimeStamp = (await ethers.provider.getBlock(blockNumber))?.timestamp as number;
-
- // We say we want to deploy our Lock contract using the deployer
- // account and passing the value and arguments.
- await deploy('Lock', {
- from: deployer,
- args: [lastBlockTimeStamp + UNLOCK_TIME],
- value: VALUE_LOCKED.toString(),
- });
-};
-
-export default func;
-
-// This tag will help us in the next section to trigger this deployment file programmatically
-func.tags = ['DeployAll'];
-```
-
-## Testing your deployment
-
-The easiest way to test your deployment is by modifying the test.
-
-Go to `Lock.ts` and include in the imports the following:
-
-```tsx
-import { ethers, deployments } from 'hardhat';
-```
-
-`deployments` will allow you to execute the deployment files from your test.
-
-Change the `before` function to look like the following:
-
-```tsx
-before(async () => {
- lastBlockTimeStamp = await time.latest();
-
- const signers = await ethers.getSigners();
- ownerSigner = signers[0];
- otherUserSigner = signers[1];
-
- await deployments.fixture(['DeployAll']);
- const lockDeployment = await deployments.get('Lock');
-
- lockInstance = Lock__factory.connect(lockDeployment.address, ownerSigner);
-});
-```
-
-Notice how you execute `deployments.fixture` and pass a tag that matches the one you specified in the deployment file (`001_deploy_lock.ts`).
-
-The deployment file is then executed and you can then reuse that functionality and simply consume the address of the newly-deployed contract by using:
-
-```tsx
-const lockDeployment = await deployments.get('Lock');
-```
-
-Reuse `Lock__factory` but use the connect function and pass the address of the newly-created contract plus a signer. Then, run `npx hardhat test` and you should get the same result:
-
-```
- Lock
- ✔ should get the unlockTime value
- ✔ should have the right ether balance
- ✔ should have the right owner
- ✔ shouldn't allow to withdraw before unlock time (51ms)
- ✔ shouldn't allow to withdraw a non owner
- ✔ should allow to withdraw an owner
-
- 6 passing (2s)
-```
-
-## Deploying to a test network
-
-Deploying to a real test network involves configuring the network parameters in the hardhat config file. You need to include parameters such as:
-
-- The JSON RPC URL
-- The account you want to use
-- Real test ether or the native Blockchain token for gas costs
-
-Include the following in the `hardhat.config.ts` file:
-
-```tsx
-const config: HardhatUserConfig = {
- solidity: '0.8.18',
- namedAccounts: {
- deployer: 0,
- },
- networks: {
- base_sepolia: {
- url: 'https://sepolia.base.org',
- accounts: {
- mnemonic: process.env.MNEMONIC ?? '',
- },
- },
- sepolia: {
- url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_SEPOLIA_KEY ?? ''}`,
- accounts: {
- mnemonic: process.env.MNEMONIC ?? '',
- },
- },
- },
-};
-```
-
-You've configured 2 networks:
-
-- base_sepolia
-- sepolia
-
-You also need to create a `.env` file with the following variables:
-
-```
-MNEMONIC=""
-ALCHEMY_SEPOLIA_KEY=
-```
-
-In order to ensure the environment variables are loaded, you need to install another package called `dotenv`:
-
-```bash
-npm install -D dotenv
-```
-
-Then, include the following in the `hardhat.config.ts` file:
-
-```tsx
-import dotenv from 'dotenv';
-
-dotenv.config();
-```
-
-Deploy to base with the following command:
-
-```bash
-npx hardhat deploy --network base_sepolia
-```
-
-After you run the command, a deployments folder appears with a newly-created deployment for `base_sepolia`:
-
-
-
-
-
-If you want to deploy to another network, change the network name as follows:
-
-```bash
-npx hardhat deploy --network sepolia
-```
-
-
-Be aware that you must have the correct environment variables for the JSON RPC URLs. For example, for Sepolia use `ALCHEMY_SEPOLIA_KEY`.
-
-
-## Conclusion
-
-In this lesson, you've learned how to deploy smart contracts using Hardhat and Hardhat-deploy. You have configured hardhat to easily deploy to multiple networks and you created deployment files to abstract this task.
-
----
-
-## See also
-
-[Solidity Docs](https://docs.soliditylang.org/en/v0.8.17/)
-[Remix Project]: https://remix-project.org/
-[Hardhat]: https://hardhat.org/
-[Hardhat Deploy]: https://github.com/wighawag/hardhat-deploy
diff --git a/docs/learn/hardhat/hardhat-deploy/installing-hardhat-deploy-vid.mdx b/docs/learn/hardhat/hardhat-deploy/installing-hardhat-deploy-vid.mdx
deleted file mode 100644
index 1cae804c6..000000000
--- a/docs/learn/hardhat/hardhat-deploy/installing-hardhat-deploy-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Installing Hardhat Deploy
-description: Install a community plugin that makes deployments easier.
-hide_table_of_contents: false
----
-
-# Installing Hardhat Deploy
-
-import { Video } from '/snippets/VideoPlayer.mdx'
-
-
diff --git a/docs/learn/hardhat/hardhat-deploy/setup-deploy-script-vid.mdx b/docs/learn/hardhat/hardhat-deploy/setup-deploy-script-vid.mdx
deleted file mode 100644
index 21baad81d..000000000
--- a/docs/learn/hardhat/hardhat-deploy/setup-deploy-script-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Setting up the Deploy Script
-description: Prepare a script to deploy your contract.
-hide_table_of_contents: false
----
-
-# Setting up the Deploy Script
-
-import { Video } from '/snippets/VideoPlayer.mdx'
-
-
diff --git a/docs/learn/hardhat/hardhat-deploy/test-network-configuration-vid.mdx b/docs/learn/hardhat/hardhat-deploy/test-network-configuration-vid.mdx
deleted file mode 100644
index 70b3e88e6..000000000
--- a/docs/learn/hardhat/hardhat-deploy/test-network-configuration-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Test Network Configuration
-description: Configure test networks.
-hide_table_of_contents: false
----
-
-# Test Network Configuration
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/hardhat/hardhat-deploy/testing-our-deployment-vid.mdx b/docs/learn/hardhat/hardhat-deploy/testing-our-deployment-vid.mdx
deleted file mode 100644
index 405a0b9fb..000000000
--- a/docs/learn/hardhat/hardhat-deploy/testing-our-deployment-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Testing Our Deployment
-description: Test the newly created deploy script.
-hide_table_of_contents: false
----
-
-# Testing Our Deployment
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/hardhat/hardhat-forking/hardhat-forking.mdx b/docs/learn/hardhat/hardhat-forking/hardhat-forking.mdx
deleted file mode 100644
index ba9941cda..000000000
--- a/docs/learn/hardhat/hardhat-forking/hardhat-forking.mdx
+++ /dev/null
@@ -1,181 +0,0 @@
----
-title: Hardhat Forking
-description: Learn how to fork
-hide_table_of_contents: false
----
-
-# Hardhat Forking
-
-In this article, you'll learn how to fork smart contracts in Ethereum mainnet using Hardhat.
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- Use Hardhat Network to create a local fork of mainnet and deploy a contract to it
-- Utilize Hardhat forking features to configure the fork for several use cases
-
----
-
-## Overview
-
-Hardhat forking is a powerful feature that allows developers to create a local replica or fork of the Ethereum network or any other EVM-compatible Blockchain. By using this feature, you can develop smart contracts that rely on smart contracts that are already deployed to a particular network.
-
-You will create a BalanceReader.sol contract that reads the USDC balance of a particular holder.
-
-In order to achieve that, you need to:
-
-- Create the BalanceReader.sol contract
-- Configure Hardhat to support forking
-- Create a test for the BalanceReader.sol contract
-
-Hardhat forking also has other capabilities like:
-
-- hardhat_impersonateAccount (useful to impersonate an account and others)
-- hardhat_stopImpersonatingAccount
-- hardhat_setNonce
-- hardhat_setBalance
-- hardhat_setCode
-- hardhat_setStorageAt
-
-Those won't be covered in this guide, however it's recommended to explore them a bit more in the following link:
-
-- https://hardhat.org/hardhat-network/guides/mainnet-forking.html
-
-## Creating the Balance Reader contract
-
-The BalanceReader contract is created as follows:
-
-```tsx
-pragma solidity 0.8.9;
-
-import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-
-contract BalanceReader {
- function getERC20BalanceOf(address _account, address _tokenAddress)
- external
- view
- returns (uint256)
- {
- // we create an instance only using the interface and the address
- return IERC20(_tokenAddress).balanceOf(_account);
- }
-}
-```
-
-You simply pass the address of an account and the address of a token, then you get and return the balance.
-
-You will need to install @openzeppelin by running:
-
-```bash
-npm install @openzeppelin/contracts
-```
-
-Then, check that everything is working correctly by running:
-
-```bash
-npx hardhat compile
-```
-
-You should get:
-
-```
-Generating typings for: 2 artifacts in dir: typechain-types for target: ethers-v6
-Successfully generated 18 typings!
-Compiled 2 Solidity files successfully
-```
-
-## Configuring Hardhat to support forking
-
-By default, Hardhat uses a network called `hardhat`. You must change its default configuration by going to the `hardhat.config.ts` file and include the following in the network:
-
-```json
-hardhat: {
- forking: {
- url: `https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_MAINNET_KEY ?? ""}`,
- enabled: true
- }
-},
-```
-
-Be aware that you need to have an `ALCHEMY_MAINNET_KEY` in your .env file. You can get one directly from [Alchemy](https://www.alchemy.com/).
-
-Also notice that forking is enabled by specifying `enabled: true`, however this value can be changed via environment variables.
-
-## Creating a test for the BalanceReader.sol contract
-
-Create a test file in the test folder called `BalanceReader.ts` and include the following:
-
-```tsx
-import { Signer } from 'ethers';
-import { ethers } from 'hardhat';
-
-import { BalanceReader, BalanceReader__factory } from '../typechain-types';
-
-describe('BalanceReader tests', () => {
- let instance: BalanceReader;
- let accounts: Signer[];
-
- // Configure the addresses we can to check balances for
- const USDC_MAINNET_ADDRESS = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; // https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
- const ARBITRUM_ONE_GATEWAY = '0xcEe284F754E854890e311e3280b767F80797180d';
- const USDC_DECIMALS = 6;
-
- it('gets arbitrum gateway balance', async () => {
- // We get signers as in a normal test
- accounts = await ethers.getSigners();
- const factory = new BalanceReader__factory(accounts[0]);
-
- // We deploy the contract to our local test environment
- instance = await factory.deploy();
-
- // Our contract will be able to check the balances of the mainnet deployed contracts and address
- const balance = await instance.getERC20BalanceOf(ARBITRUM_ONE_GATEWAY, USDC_MAINNET_ADDRESS);
- const balanceAsString = ethers.utils.formatUnits(balance, USDC_DECIMALS);
-
- console.log(
- 'The USDC Balance of Arbitrum Gateway is $',
- Number(balanceAsString).toLocaleString(),
- );
- });
-});
-```
-
-In this example, the [USDC address](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48) is used and since USDC is an ERC-20 token, you can explore the token holders of that particular token directly in Etherscan:
-
-
-
-
-
-Or, visit https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#balances, where you can see, at the time or writing, Arbitrum ONE Gateway is the top token holder.
-
-Then, run the following command:
-
-```bash
-npx hardhat test ./test/BalanceReader.ts
-```
-
-You should get:
-
-```
-BalanceReader tests
-The USDC Balance of Arbitrum Gateway is $ 1,116,923,836.506
- ✔ gets arbitrum gateway balance (4345ms)
-
- 1 passing (4s)
-```
-
-## Conclusion
-
-In this lesson, you've learned how to use hardhat forking capabilities to test smart contracts. You learned how contracts can interact with already-deployed contracts in an easy way.
-
----
-
-## See also
-
-[Solidity Docs]: https://docs.soliditylang.org/en/v0.8.17/
-[Remix Project]: https://remix-project.org/
-[Hardhat Deploy]: https://github.com/wighawag/hardhat-deploy
-[Hardhat Forking]: https://hardhat.org/hardhat-network/docs/guides/forking-other-networks
diff --git a/docs/learn/hardhat/hardhat-forking/mainnet-forking-vid.mdx b/docs/learn/hardhat/hardhat-forking/mainnet-forking-vid.mdx
deleted file mode 100644
index 39abbda2e..000000000
--- a/docs/learn/hardhat/hardhat-forking/mainnet-forking-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Forking Mainnet
-description: Create a copy of the mainnet to run advanced tests.
-hide_table_of_contents: false
----
-
-# Forking Mainnet
-
-import { Video } from '/snippets/VideoPlayer.mdx'
-
-
diff --git a/docs/learn/hardhat/hardhat-setup-overview/creating-a-project-vid.mdx b/docs/learn/hardhat/hardhat-setup-overview/creating-a-project-vid.mdx
deleted file mode 100644
index e52aa960a..000000000
--- a/docs/learn/hardhat/hardhat-setup-overview/creating-a-project-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Creating a Project
-description: Using Hardhat to start a new project.
-hide_table_of_contents: false
----
-
-# Creating a Project
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/hardhat/hardhat-setup-overview/hardhat-overview-vid.mdx b/docs/learn/hardhat/hardhat-setup-overview/hardhat-overview-vid.mdx
deleted file mode 100644
index 2c0ccd707..000000000
--- a/docs/learn/hardhat/hardhat-setup-overview/hardhat-overview-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Overview
-description: An introduction to Hardhat
-hide_table_of_contents: false
----
-
-# Overview
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/hardhat/hardhat-setup-overview/hardhat-setup-overview-sbs.mdx b/docs/learn/hardhat/hardhat-setup-overview/hardhat-setup-overview-sbs.mdx
deleted file mode 100644
index c45966a0b..000000000
--- a/docs/learn/hardhat/hardhat-setup-overview/hardhat-setup-overview-sbs.mdx
+++ /dev/null
@@ -1,161 +0,0 @@
----
-title: Setup and Overview
-description: An overview of hardhat, a smart contract development framework
-hide_table_of_contents: false
----
-
-# Setup and Overview
-
-In this article, you'll learn about Hardhat: a development framework to create, test, and deploy smart contracts to Ethereum and other supported EVM chains.
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- Install and create a new Hardhat project with Typescript support
-- Describe the organization and folder structure of a Hardhat project
-- List the use and properties of hardhat.config.ts
-
----
-
-## Overview
-
-[Hardhat] is a development environment that allows you to develop and test Solidity on your local machine. It includes debugging and unit testing tools, and has an ecosystem of third-party-developed plugins that ease development and deployment.
-
-Among other things, these plugins can help you deploy contracts, see the size of your compiled byte-code, and even see unit test coverage.
-
-## Installing Hardhat and creating a new project
-
-As a pre-requisite to start developing smart contracts with Hardhat, Node.js must be installed.
-
-You can then simply type `npx hardhat init`, which provides a set of options to bootstrap a Hardhat project:
-
-```
-888 888 888 888 888
-888 888 888 888 888
-888 888 888 888 888
-8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
-888 888 "88b 888P" d88" 888 888 "88b "88b 888
-888 888 .d888888 888 888 888 888 888 .d888888 888
-888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
-888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
-
-👷 Welcome to Hardhat v2.11.2 👷
-
-? What do you want to do? …
-❯ Create a JavaScript project
- Create a TypeScript project
- Create an empty hardhat.config.js
- Quit
-```
-
-You are encouraged to select **Create a TypeScript project**, since it provides you with some benefits such as static typing that can reduce the number of errors during development.
-
-You can then enter 'yes' for the remaining options, which include installing the `@nomicfoundation/hardhat-toolbox` package that contains some of the most used Hardhat plugins.
-
-```
-✔ What do you want to do? · Create a TypeScript project
-✔ Hardhat project root: · {any location}
-✔ Do you want to add a .gitignore? (Y/n) · y
-✔ Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) · y
-```
-
-### Anatomy of a Hardhat project
-
-After you complete the previous step, the folder structure looks like the following:
-
-- contracts # contracts will go here
-- hardhat.config.ts # configuration file for hardhat
-- node_modules # node.js package folder
-- package-lock.json # node.js package lock file
-- package.json # node.js package file
-- scripts # place the scripts here
-- test # place the tests here
-- tsconfig.json # typescript configuration file
-
-It is also common to save hardhat tasks in a `task` folder.
-
-It is important to mention that all these paths are fully configurable in the `hardhat.config.ts` file. You can specify a different folder for the contracts, such as `src`.
-
-### Configuration
-
-You can configure the Hardhat environment in the `hardhat.config.ts` file.
-
-Since the project uses Typescript, you have the benefit of using static typing.
-
-The following is the default configuration:
-
-```tsx
-import { HardhatUserConfig } from 'hardhat/config';
-import '@nomicfoundation/hardhat-toolbox';
-
-const config: HardhatUserConfig = {
- solidity: '0.8.17',
-};
-
-export default config;
-```
-
-You can configure aspects such as:
-
-- default network
-- networks
-- solidity
-- paths
-- mocha
-
-For example:
-
-```tsx
-import { HardhatUserConfig } from 'hardhat/config';
-import '@nomicfoundation/hardhat-toolbox';
-
-const config: HardhatUserConfig = {
- defaultNetwork: 'base',
- networks: {
- base_sepolia: {
- url: 'https://sepolia.base.org',
- accounts: [''],
- },
- sepolia: {
- url: 'https://sepolia.infura.io/v3/',
- accounts: ['', ''],
- },
- },
- solidity: {
- version: '0.8.17',
- settings: {
- optimizer: {
- enabled: true,
- runs: 200,
- },
- },
- },
- paths: {
- sources: './contracts',
- tests: './test',
- cache: './cache',
- artifacts: './artifacts',
- },
-};
-
-export default config;
-```
-
-## Compiling smart contracts
-
-At this point, you should have a Hardhat project up and running to start developing smart contracts. You may notice Hardhat includes a sample contract called `Lock.sol`.
-
-To run your first command, enter `npx hardhat compile`, which compiles the smart contracts and generates the correct [artifacts](https://hardhat.org/hardhat-runner/docs/advanced/artifacts) that includes the bytecode and ABI.
-
-After running the `npx hardhat compile` command, you should see a new folder named artifacts. This folder contains each contract name as a folder and a
-`{ContractName}.json` file.
-
----
-
-[Solidity Docs]: https://docs.soliditylang.org/en/v0.8.17/
-[Remix Project]: https://remix-project.org/
-[Hardhat]: https://hardhat.org/
-[Compilation Artifacts]: https://hardhat.org/hardhat-runner/docs/advanced/artifacts
diff --git a/docs/learn/hardhat/hardhat-testing/contract-abi-and-testing-vid.mdx b/docs/learn/hardhat/hardhat-testing/contract-abi-and-testing-vid.mdx
deleted file mode 100644
index 2cc49fcc6..000000000
--- a/docs/learn/hardhat/hardhat-testing/contract-abi-and-testing-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Contract ABIs and Testing
-description: Learn how the contract ABI is related to writing tests.
-hide_table_of_contents: false
----
-
-# Contract ABIs and Testing
-
-import { Video } from '/snippets/VideoPlayer.mdx'
-
-
diff --git a/docs/learn/hardhat/hardhat-testing/hardhat-testing-sbs.mdx b/docs/learn/hardhat/hardhat-testing/hardhat-testing-sbs.mdx
deleted file mode 100644
index 804a4578a..000000000
--- a/docs/learn/hardhat/hardhat-testing/hardhat-testing-sbs.mdx
+++ /dev/null
@@ -1,342 +0,0 @@
----
-title: Testing with Hardhat and Typechain
-description: Testing smart contracts with Hardhat and Typechain.
-hide_table_of_contents: false
----
-
-# Testing with Hardhat and Typechain
-
-In this article, you'll learn how to test smart contracts with Hardhat and Typechain.
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- Set up TypeChain to enable testing
-- Write unit tests for smart contracts using Mocha, Chai, and the Hardhat Toolkit
-- Set up multiple signers and call smart contract functions with different signers
-
----
-
-## Overview
-
-Testing is an important aspect of software development and developing smart contracts is no different. In fact, you need to be more careful because
-smart contracts usually manage money and live in an adversarial environment, where anyone can see the code and interact with your smart contract. This means you can expect bad actors to try to exploit your smart contracts.
-
-## Setup Typechain
-
-In the previous guide, you created a new project using the `init` command that by default installs `@nomicfoundation/hardhat-toolbox`. This package already contains Typechain, which is a plugin that generates static types for your smart contracts. This means you can interact with your contracts and get immediate feedback about the parameters received by a particular function and the functions of a smart contract.
-
-The best way to see its true potential is to start writing tests.
-
-After compiling the hardhat project in the previous lesson, a new folder called `typechain-types` was created, which Typechain is already installed and running.
-
-### Writing your first unit test with Typechain
-
-Hardhat includes a sample smart contract named `Lock.sol` and a sample test inside the test folder named `Lock.ts`.
-
-In the following, you reuse this smart contract but rewrite the test using Typechain.
-
-To remove the body of the `Lock.ts` file:
-
-```tsx
-import { expect } from 'chai';
-import { ethers } from 'hardhat';
-
-describe('Lock', function () {});
-```
-
-Then, import two files from `typechain-types`, `Lock`, and `Lock__Factory`.
-
-Typechain always creates two files per contract. The first one `Lock` refers to the type and functions of a particular contract. `Lock__Factory` is used to deploy the Lock contract or to create instances of a particular contract.
-
-The `Lock.sol` contract allows the creator to lock Ether until an unlock time has passed.
-
-Notice the constructor has a payable keyword:
-
-```tsx
-constructor(uint _unlockTime) payable {
- require(
- block.timestamp < _unlockTime,
- "Unlock time should be in the future"
- );
-
- unlockTime = _unlockTime;
- owner = payable(msg.sender);
- }
-```
-
-This means the contract is expecting to receive an amount of ether.
-
-Next, test the following:
-
-- The unlock time value
-- The value locked during creation
-- The owner address
-- The withdraw function
-
-
-
-Reveal code
-
-Start with the value locked, however you must set up a `before` function, which will run before each test case.
-
-Then, include some new imports and variables:
-
-```tsx
-import { expect } from 'chai';
-import { ethers } from 'hardhat';
-
-// A helper utility to get the timestamp.
-import { time } from '@nomicfoundation/hardhat-network-helpers';
-
-// We import this type to have our signers typed.
-import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';
-
-// Types from typechain
-import { Lock__factory, Lock } from '../typechain-types';
-
-describe('Lock', function () {
- // This represents the time in the future we expect to release the funds locked.
- const UNLOCK_TIME = 10000;
-
- // The amount of ether we plan to lock.
- const VALUE_LOCKED = ethers.parseEther('0.01');
-
- // This variable will store the last block timestamp.
- let lastBlockTimeStamp: number;
-
- // Typechain allow us to type an instance of the Lock contract.
- let lockInstance: Lock;
-
- // This is the Signer of the owner.
- let ownerSigner: SignerWithAddress;
-
- // A non owner signed is useful to test non owner transactions.
- let otherUserSigner: SignerWithAddress;
-
- before(async () => {
- // We get the latest block.timestamp using the latest function of time.
- lastBlockTimeStamp = await time.latest();
-
- // Hardhat provide us with some sample signers that simulate Ethereum accounts.
- const signers = await ethers.getSigners();
-
- // We simply assign the first signer to ownerSigner
- ownerSigner = signers[0];
-
- // We assign the second signer to otherUserSigner
- otherUserSigner = signers[1];
-
- // We estimate unlockTime to be the last time stamp plus UNLOCK_TIME
- const unlockTime = lastBlockTimeStamp + UNLOCK_TIME;
-
- // Notice how we use the Lock__factory and pass a signer. Then we deploy by passing the unlockTime and the amount of ether we will lock.
- lockInstance = await new Lock__factory(ownerSigner).deploy(unlockTime, {
- value: VALUE_LOCKED,
- });
- });
-});
-```
-
-
-
-### Testing `unlockTime`
-
-Next, you include test cases after the `before` function.
-
-The first test case should verify that the `unlockTime` variable is correct.
-
-
-
-Reveal code
-
-```tsx
-it('should get the unlockTime value', async () => {
- // we get the value from the contract
- const unlockTime = await lockInstance.unlockTime();
-
- // We assert against the
- expect(unlockTime).to.equal(lastBlockTimeStamp + UNLOCK_TIME);
-});
-```
-
-Notice how autocomplete appears after entering `lockInstance`:
-
-
-
-
-
-You can simply run `npx hardhat test` and then get:
-
-```
- Lock
- ✔ should get the unlockTime value
-
- 1 passing (1s)
-```
-
-
-
-### Testing Ether balance
-
-In order to get the balance of your `Lock` contract, you simply call `ethers.provider.getBalance`.
-
-Create a new test case:
-
-
-
-Reveal code
-
-```tsx
-it('should have the right ether balance', async () => {
- // Get the Lock contract address
- const lockInstanceAddress = await lockInstance.getAddress();
-
- // Get the balance using ethers.provider.getBalance
- const contractBalance = await ethers.provider.getBalance(lockInstanceAddress);
-
- // We assert the balance against the VALUE_LOCKED we initially sent
- expect(contractBalance).to.equal(VALUE_LOCKED);
-});
-```
-
-
-
-
-Then, run `npx hardhat test` and you should get:
-
-```
- Lock
- ✔ should get the unlockTime value
- ✔ should have the right ether balance
-
- 2 passing (1s)
-```
-
-### Testing `owner`
-
-Similar to the previous test cases, you can verify that the owner is correct.
-
-
-
-Reveal code
-
-```tsx
-it('should have the right owner', async () => {
- // Notice ownerSigned has an address property
- expect(await lockInstance.owner()).to.equal(ownerSigner.address);
-});
-```
-
-
-
-
-Then, run `npx hardhat test` and you should get:
-
-```
- Lock
- ✔ should get the unlockTime value
- ✔ should have the right ether balance
- ✔ should have the right owner
-
- 3 passing (1s)
-```
-
-### Testing withdraw
-
-Testing withdrawal is more complex because you need to assert certain conditions, such as:
-
-- The owner cannot withdraw before the unlock time.
-- Only the owner can withdraw.
-- The withdraw function works as expected.
-
-Hardhat allow you to test reverts with a set of custom matchers.
-
-For example, the following code checks that an attempt to call the function `withdraw` reverts with a particular message:
-
-```tsx
-it('shouldn"t allow to withdraw before unlock time', async () => {
- await expect(lockInstance.withdraw()).to.be.revertedWith("You can't withdraw yet");
-});
-```
-
-In addition, Hardhat also allows you to manipulate the time of the environment where the tests are executed. You can think of it as a Blockchain that is running before the tests and then the tests are executed against it.
-
-You can modify `the block.timestamp` by using the time helper:
-
-```tsx
-it('shouldn"t allow to withdraw a non owner', async () => {
- const newLastBlockTimeStamp = await time.latest();
-
- // We set the next block time stamp using this helper.
- // We assign a value further in the future.
- await time.setNextBlockTimestamp(newLastBlockTimeStamp + UNLOCK_TIME);
-
- // Then we try to withdraw using other user signer. Notice the .connect function that is useful
- // to create and instance but have the msg.sender as the new signer.
- const newInstanceUsingAnotherSigner = lockInstance.connect(otherUserSigner);
-
- // We attempt to withdraw, but since the sender is not the owner, it will revert.
- await expect(newInstanceUsingAnotherSigner.withdraw()).to.be.revertedWith("You aren't the owner");
-});
-```
-
-Finally, test that the owner can withdraw. You can manipulate the time similarly to the previous test case but you won't change the signer and will assert the new balances.
-
-
-
-Reveal code
-
-```tsx
-it('should allow to withdraw an owner', async () => {
- const balanceBefore = await ethers.provider.getBalance(await lockInstance.getAddress());
-
- // Its value will be the one we lock at deployment time.
- expect(balanceBefore).to.equal(VALUE_LOCKED);
-
- const newLastBlockTimeStamp = await time.latest();
-
- // We increase time
- await time.setNextBlockTimestamp(newLastBlockTimeStamp + UNLOCK_TIME);
-
- // Attempt to withdraw
- await lockInstance.withdraw();
-
- // Get new balance and assert that is 0
- const balanceAfter = await ethers.provider.getBalance(await lockInstance.getAddress());
- expect(balanceAfter).to.equal(0);
-});
-```
-
-
-
-
-
-You can then run `npx hardhat test` and you should get:
-
-```
- Lock
- ✔ should get the unlockTime value
- ✔ should have the right ether balance
- ✔ should have the right owner
- ✔ shouldn"t allow to withdraw before unlock time (51ms)
- ✔ shouldn"t allow to withdraw a non owner
- ✔ should allow to withdraw an owner
-
- 6 passing (2s)
-```
-
-## Conclusion
-
-In this lesson, you've learned how to test smart contracts using Hardhat and Typechain.
-
----
-
-## See also
-
-[Solidity Docs](https://docs.soliditylang.org/en/v0.8.17/)
-[Remix Project]: https://remix-project.org/
-[Hardhat]: https://hardhat.org/
diff --git a/docs/learn/hardhat/hardhat-testing/testing-overview-vid.mdx b/docs/learn/hardhat/hardhat-testing/testing-overview-vid.mdx
deleted file mode 100644
index a4c4e6fa5..000000000
--- a/docs/learn/hardhat/hardhat-testing/testing-overview-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Testing Overview
-description: An overview of writing tests in Hardhat.
-hide_table_of_contents: false
----
-
-# Testing Overview
-
-import { Video } from '/snippets/VideoPlayer.mdx'
-
-
diff --git a/docs/learn/hardhat/hardhat-testing/writing-tests-vid.mdx b/docs/learn/hardhat/hardhat-testing/writing-tests-vid.mdx
deleted file mode 100644
index 4768b8e1e..000000000
--- a/docs/learn/hardhat/hardhat-testing/writing-tests-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Writing Tests
-description: An introduction to writing tests.
-hide_table_of_contents: false
----
-
-# Writing Tests
-
-import { Video } from '/snippets/VideoPlayer.mdx'
-
-
diff --git a/docs/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage.mdx b/docs/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage.mdx
deleted file mode 100644
index 28ce7bd88..000000000
--- a/docs/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage.mdx
+++ /dev/null
@@ -1,240 +0,0 @@
----
-title: 'Hardhat: Analyzing the test coverage of smart contracts'
-slug: /hardhat-test-coverage
-description: A tutorial that teaches how to profile the test coverage of your smart contracts using Hardhat and the Solidity Coverage plugin.
-author: Edson Alcala
----
-
-# Analyzing the test coverage of smart contracts using Hardhat
-
-In this tutorial, you'll learn how to profile the test coverage of your smart contracts with [Hardhat] and the [Solidity Coverage] community plugin.
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-- Use the Solidity Coverage plugin to analyze the coverage of your test suite
-- Increase the coverage of your test suite
-
-## Overview
-
-The Solidity Coverage plugin allows you to analyze and visualize the coverage of your smart contracts' test suite. This enables you to see what portions of your smart contract are being tested and what areas may have been overlooked. It's an indispensable plugin for developers seeking to fortify their testing practices and ensure robust smart contract functionality.
-
-## Setting up the Solidity Coverage plugin
-
-The Solidity Coverage plugin is integrated into the Hardhat toolbox package, which is installed by default when you use the `npx hardhat init` command.
-
-To install manually, run `npm install -D solidity-coverage`.
-
-Then, import `solidity-coverage` in `hardhat.config.ts`:
-
-```solidity
-import "solidity-coverage"
-```
-
-Once the installation completes either manually or via the default Hardhat template, the task `coverage` becomes available via the `npx hardhat coverage` command.
-
-## My first test coverage
-
-Review the following contract and test suite (You'll recognize these if you completed the [Hardhat testing lesson] in our [Base Learn] series).
-
-Contract:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Lock {
- uint public unlockTime;
- address payable public owner;
-
- event Withdrawal(uint amount, uint when);
-
- constructor(uint _unlockTime) payable {
- require(
- block.timestamp < _unlockTime,
- "Unlock time should be in the future"
- );
-
- unlockTime = _unlockTime;
- owner = payable(msg.sender);
- }
-
- function withdraw() public {
- require(block.timestamp >= unlockTime, "You can't withdraw yet");
- require(msg.sender == owner, "You aren't the owner");
-
- emit Withdrawal(address(this).balance, block.timestamp);
-
- owner.transfer(address(this).balance);
- }
-}
-```
-
-`Lock.test.ts`:
-
-```solidity
-import { expect } from "chai";
-import { ethers } from "hardhat";
-import { time } from "@nomicfoundation/hardhat-network-helpers";
-import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'
-import { Lock__factory, Lock} from '../typechain-types'
-
-describe("Lock Tests", function () {
- const UNLOCK_TIME = 10000;
- const VALUE_LOCKED = ethers.parseEther("0.01");
-
- let lastBlockTimeStamp: number;
- let lockInstance: Lock;
- let ownerSigner: SignerWithAddress
- let otherUserSigner: SignerWithAddress;
-
- before(async() => {
- lastBlockTimeStamp = await time.latest()
- const signers = await ethers.getSigners()
- ownerSigner = signers[0]
- otherUserSigner= signers[1]
-
- const unlockTime = lastBlockTimeStamp + UNLOCK_TIME;
-
- lockInstance = await new Lock__factory(ownerSigner).deploy(unlockTime, {
- value: VALUE_LOCKED
- })
- })
-
- it('should get the unlockTime value', async() => {
- const unlockTime = await lockInstance.unlockTime();
-
- expect(unlockTime).to.equal(lastBlockTimeStamp + UNLOCK_TIME)
- })
-
- it('should have the right ether balance', async() => {
- const lockInstanceAddress = await lockInstance.getAddress()
-
- const contractBalance = await ethers.provider.getBalance(lockInstanceAddress)
-
- expect(contractBalance).to.equal(VALUE_LOCKED)
- })
-
- it('should have the right owner', async()=> {
- expect(await lockInstance.owner()).to.equal(ownerSigner.address)
- })
-
- it('should not allow to withdraw before unlock time', async()=> {
- await expect(lockInstance.withdraw()).to.be.revertedWith("You can't withdraw yet")
- })
-
- it('should not allow to withdraw a non owner', async()=> {
- const newLastBlockTimeStamp = await time.latest()
-
- await time.setNextBlockTimestamp(newLastBlockTimeStamp + UNLOCK_TIME)
-
- const newInstanceUsingAnotherSigner = lockInstance.connect(otherUserSigner)
-
- await expect(newInstanceUsingAnotherSigner.withdraw()).to.be.revertedWith("You aren't the owner")
- })
-
- it('should allow to withdraw a owner', async()=> {
- const balanceBefore = await ethers.provider.getBalance(await lockInstance.getAddress());
-
- expect(balanceBefore).to.equal(VALUE_LOCKED)
-
- const newLastBlockTimeStamp = await time.latest()
-
- await time.setNextBlockTimestamp(newLastBlockTimeStamp + UNLOCK_TIME)
-
- await lockInstance.withdraw();
-
- const balanceAfter = await ethers.provider.getBalance(await lockInstance.getAddress());
- expect(balanceAfter).to.equal(0)
- })
-});
-```
-
-If you run `npx hardhat coverage`, you should get:
-
-```terminal
- Lock Tests
- ✔ should get the unlockTime value
- ✔ should have the right ether balance
- ✔ should have the right owner
- ✔ shouldn't allow to withdraw before unlock time
- ✔ shouldn't allow to withdraw a non owner
- ✔ should allow to withdraw a owner
-
- 6 passing (195ms)
-
-------------|----------|----------|----------|----------|----------------|
-File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-------------|----------|----------|----------|----------|----------------|
- contracts/ | 100 | 83.33 | 100 | 100 | |
- Lock.sol | 100 | 83.33 | 100 | 100 | |
-------------|----------|----------|----------|----------|----------------|
-All files | 100 | 83.33 | 100 | 100 | |
-------------|----------|----------|----------|----------|----------------|
-```
-
-Which then gives you a report of the test coverage of your test suite. Notice there is a new folder called `coverage`, which was generated by the `solidity-coverage` plugin. Inside the `coverage` folder there is a `index.html` file. Open it in a browser, you'll see a report similar to the following:
-
-
-
-
-
-## Increasing test coverage
-
-Although the coverage of the previous test suite is almost perfect, there is one missing branch when creating the contract. Because you have not tested the condition that the `_unlockTime` has to be greater than the `block.timestamp`:
-
-```solidity
-require(
- block.timestamp < _unlockTime,
- "Unlock time should be in the future"
- );
-```
-
-In order to increase the coverage, include a new test with the following:
-
-```solidity
- it('should verify the unlock time to be in the future', async () => {
- const newLockInstance = new Lock__factory(ownerSigner).deploy(lastBlockTimeStamp, {
- value: VALUE_LOCKED
- })
-
- await expect(newLockInstance).to.be.revertedWith("Unlock time should be in the future")
- })
-```
-
-Then, run `npx hardhat coverage` and you should get:
-
-```solidity
- Lock Tests
- ✔ should verify the unlock time to be in the future (39ms)
- ✔ should get the unlockTime value
- ✔ should have the right ether balance
- ✔ should have the right owner
- ✔ shouldn't allow to withdraw before unlock time
- ✔ shouldn't allow to withdraw a non owner
- ✔ should allow to withdraw a owner
-
- 7 passing (198ms)
-
-------------|----------|----------|----------|----------|----------------|
-File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
-------------|----------|----------|----------|----------|----------------|
- contracts/ | 100 | 100 | 100 | 100 | |
- Lock.sol | 100 | 100 | 100 | 100 | |
-------------|----------|----------|----------|----------|----------------|
-All files | 100 | 100 | 100 | 100 | |
-------------|----------|----------|----------|----------|----------------|
-```
-
-## Conclusion
-
-In this tutorial, you've learned how to profile and analyze the test coverage of your smart contracts' test suite. You learned how to visualize the coverage report and improve the coverage of the test suite by using the Solidity Coverage plugin.
-
-## See also
-
-[Hardhat]: https://hardhat.org/
-[Solidity Coverage]: https://github.com/sc-forks/solidity-coverage
-[Hardhat testing lesson]: https://docs.base.org/base-learn/docs/hardhat-testing/hardhat-testing-sbs
-[Base Learn]: https://base.org/learn
-
diff --git a/docs/learn/hardhat/hardhat-tools-and-testing/debugging-smart-contracts.mdx b/docs/learn/hardhat/hardhat-tools-and-testing/debugging-smart-contracts.mdx
deleted file mode 100644
index 9d026404b..000000000
--- a/docs/learn/hardhat/hardhat-tools-and-testing/debugging-smart-contracts.mdx
+++ /dev/null
@@ -1,407 +0,0 @@
----
-title: 'Hardhat: Debugging smart contracts'
-slug: /hardhat-debugging
-description: A tutorial that teaches how to debug your smart contracts using Hardhat.
-author: Edson Alcala
----
-
-# Debugging smart contracts using Hardhat
-
-In this tutorial, you'll learn how to debug your smart contracts using the built-in debugging capabilities of Hardhat.
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-- Use `console.log` to write debugging logs
-- List common errors and their resolutions
-- Determine if an error is a contract error or an error in the test
-
-## Overview
-
-Debugging smart contracts can be a challenging task, especially when dealing with decentralized applications and blockchain technology. Hardhat provides powerful tools to simplify the debugging process.
-
-In this tutorial, you will explore the essential debugging features offered by Hardhat and learn how to effectively identify and resolve common errors in your smart contracts.
-
-## Your first `console.log`
-
-One of the key features of Hardhat is the ability to use `console.log` for writing debugging logs in your smart contracts. In order to use it, you must include `hardhat/console.sol` in the contract you wish to debug.
-
-In the following contract `Lock.sol` for example, you include `hardhat/console.sol` by importing it and adding a few `console.log`s in the constructor with the text "Creating" and the Ether balance of the contract. This can help you not only with tracking that the contract was created successfully but also, more importantly, with the ability to include additional logs such as the balance of the contract after it was created:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "hardhat/console.sol";
-
-contract Lock {
- uint256 public unlockTime;
- address payable public owner;
-
- constructor(uint _unlockTime) payable {
- require(
- block.timestamp < _unlockTime,
- "Unlock time should be in the future"
- );
-
- unlockTime = _unlockTime;
- owner = payable(msg.sender);
-
- console.log("Creating");
- console.log("Balance", address(this).balance);
- }
-}
-```
-
-In order to test it, you need to create a new file in the `test` folder called `Lock.test.ts` with the following content:
-
-```solidity
-import { expect } from "chai";
-import { ethers } from "hardhat";
-
-import { time } from "@nomicfoundation/hardhat-network-helpers";
-import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'
-
-import { Lock__factory, Lock } from '../typechain-types'
-
-describe("Lock Tests", function () {
- const UNLOCK_TIME = 10000;
- const VALUE_LOCKED = ethers.parseEther("0.01");
-
- let lastBlockTimeStamp: number;
- let lockInstance: Lock;
- let ownerSigner: SignerWithAddress
-
- before(async () => {
- lastBlockTimeStamp = await time.latest()
-
- const signers = await ethers.getSigners()
- ownerSigner = signers[0]
-
- lockInstance = await new Lock__factory().connect(ownerSigner).deploy(lastBlockTimeStamp + UNLOCK_TIME, {
- value: VALUE_LOCKED
- })
- })
-
- it('should get the unlockTime value', async () => {
- expect(await lockInstance.unlockTime()).to.equal(lastBlockTimeStamp + UNLOCK_TIME)
- })
-});
-```
-
-Notice that a single test is included in order to get proper logs. However, you're only interested in the creation process that happens in the `before` hook. Then, you can run:
-
-```bash
-npx hardhat test
-```
-
-You should see the following in the terminal:
-
-```bash
- Lock
-Creating
-Balance 10000000000000000
- ✔ should get the unlockTime value
-```
-
-The terminal shows the text "Creating" and the balance (which is 0.01 Ether) because during the creation, you are depositing Ether in the smart contract via the `value` property.
-
-### A note about `console.log`
-
-In the previous example, you used `console.log` to include some debugging logs. Be aware that the `console.log` version of Solidity is limited compared to the ones that are provided in other programming languages, where you can log almost anything.
-
-`Console.log` can be called with up to four parameters of the following types:
-
-- uint
-- string
-- bool
-- address
-
-Hardhat includes other `console` functions, such as:
-
-- console.logInt(int i)
-- console.logBytes(bytes memory b)
-- console.logBytes1(bytes1 b)
-- console.logBytes2(bytes2 b)
-- ...
-- console.logBytes32(bytes32 b)
-
-These log functions are handy when the type you intend to log doesn't fall within the default accepted types of `console.log`. For further details, refer to the official [console.log] documentation.
-
-## Identifying common errors
-
-While debugging your smart contracts, it's crucial to be familiar with common errors that can arise during development. Recognizing these errors and knowing how to resolve them is an important skill.
-
-In our [Base Learn] series of tutorials, we cover a few compile-time errors in [Error Triage]. Other errors, such as `reverts` or `index out of bounds errors` can be unexpected during the runtime of the smart contract.
-
-The following explores typical techniques to debug these types of errors.
-
-### Revert errors
-
-When a transaction fails due to a `require` or `revert` statement, you'll need to diagnose why the condition isn't met and then resolve it. Typically, this involves verifying input parameters, state variables, or contract conditions.
-
-The following is the `Lock.sol` contract with a require statement that validates that the parameter you are passing (`_unlockTime`) must be greater than the current `block.timestamp`.
-
-A simple solution to troubleshoot this error is to log the value of `block.timestamp` and `_unlockTime`, which will help you compare these values and then ensure that you are passing the correct ones:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "hardhat/console.sol";
-
-contract Lock {
- uint public unlockTime;
- address payable public owner;
-
- // event Withdrawal(uint amount, uint when);
-
- constructor(uint _unlockTime) payable {
- console.log("_unlockTime",_unlockTime);
- console.log("block.timestamp",block.timestamp);
- require(
- block.timestamp < _unlockTime,
- "Unlock time should be in the future"
- );
-
- unlockTime = _unlockTime;
- owner = payable(msg.sender);
-
- console.log("Creating");
- console.log("Balance", address(this).balance);
- }
-}
-```
-
-When you run the tests with `npx hardhat test`, you'll then see the following:
-
-```bash
-Lock Tests
-_unlockTime 1697493891
-block.timestamp 1697483892
-Creating
-Balance 10000000000000000
- ✔ should get the unlockTime value
-```
-
-You are now able to see the `block.timestamp` and the value you are passing, which makes it easier to detect the error.
-
-### Unintended behavior errors
-
-Unintended behavior errors occur when you introduce unexpected behavior into the codebase due to a misunderstanding in the way Solidity works.
-
-In the following example, `LockCreator` is a contract that allows anybody to deploy a `Lock.sol` instance. However, the `LockCreator` contains an error: the `createLock` functions are able to accept Ether to be locked but the amount sent is not being transferred to the `Lock` contract:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "hardhat/console.sol";
-
-import {Lock} from "./Lock.sol";
-
-contract LockCreator {
-
- Lock[] internal locks;
-
- // Example of bad code, do not use
- function createLock(uint256 _unlockTime) external payable {
- Lock newLock = new Lock(_unlockTime);
- locks.push(newLock);
- }
-}
-```
-
-You can create a test file `LockCreator.test.ts` that can identify the error and then solve it:
-
-```solidity
-import { ethers } from "hardhat";
-
-import { time } from "@nomicfoundation/hardhat-network-helpers";
-import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'
-
-import { LockCreator, LockCreator__factory } from '../typechain-types'
-
-describe("LockCreator Tests", function () {
- const UNLOCK_TIME = 10000;
- const VALUE_LOCKED = ethers.parseEther("0.01");
-
- let lastBlockTimeStamp: number;
- let lockInstance: LockCreator;
- let ownerSigner: SignerWithAddress
-
- before(async () => {
- const signers = await ethers.getSigners()
- ownerSigner = signers[0]
-
- lockInstance = await new LockCreator__factory().connect(ownerSigner).deploy()
- })
-
- it('should create a lock', async () => {
- lastBlockTimeStamp = await time.latest()
- await lockInstance.createLock(lastBlockTimeStamp + UNLOCK_TIME, {
- value: VALUE_LOCKED
- })
- })
-});
-```
-
-The following appears in the terminal where you can see the balance is `0`:
-
-```bash
- LockCreator Tests
-Creating
-Balance 0
- ✔ should create a lock (318ms)
-```
-
-Although this issue can be avoided by adding more test cases with proper assertions, the re-transfer of Ether from the `LockCreator` was something you may have overlooked.
-
-The solution is to modify the `createLock` function with:
-
-```solidity
-function createLock(uint256 _unlockTime) external payable {
- Lock newLock = new Lock{ value: msg.value}(_unlockTime);
- locks.push(newLock);
-}
-```
-
-### Out-of-bounds errors
-
-Attempting to access arrays at an invalid position can also cause errors.
-
-If you wish to retrieve all the `Lock` contract instances being created in the previous example, you can make the `locks` array public. In order to illustrate this example, though, you can create a custom function called `getAllLocks`:
-
-```solidity
-contract LockCreator {
- //
- // rest of the code..
- //
- function getAllLocks() external view returns(Lock[] memory result) {
- result = new Lock[](locks.length);
- for(uint i = 0; i <= locks.length; i++){
- result[i] = locks[i];
- }
- }
-}
-```
-
-The function can be tested with the following test:
-
-```solidity
-describe("LockCreator Tests", function () {
- const UNLOCK_TIME = 10000;
- const VALUE_LOCKED = ethers.parseEther("0.01");
-
- let lastBlockTimeStamp: number;
- let lockInstance: LockCreator;
- let ownerSigner: SignerWithAddress
-
- before(async () => {
- const signers = await ethers.getSigners()
- ownerSigner = signers[0]
-
- lockInstance = await new LockCreator__factory().connect(ownerSigner).deploy()
-
- lastBlockTimeStamp = await time.latest()
-
- await lockInstance.createLock(lastBlockTimeStamp + UNLOCK_TIME, {
- value: VALUE_LOCKED
- })
- })
-
- it('should get all locks', async () => {
- const allLocks = await lockInstance.getAllLocks()
-
- console.log("all locks", allLocks)
- })
-});
-```
-
-Which will then throw an error:
-
-```bash
-LockCreator Tests
-Creating
-Balance 10000000000000000
- 1) should get all locks
-
- 0 passing (3s)
- 1 failing
-
- 1) LockCreator Tests
- should get all locks:
- Error: VM Exception while processing transaction: reverted with panic code 0x32 (Array accessed at an out-of-bounds or negative index)
-```
-
-You can include some debugging logs to identify the issue:
-
-```solidity
- function getAllLocks() external view returns(Lock[] memory result) {
- result = new Lock[](locks.length);
-
- console.log("locks length %s", locks.length);
-
- for(uint i = 0; i <= locks.length; i++){
- console.log("Locks index %s", i);
- result[i] = locks[i];
- }
-}
-```
-
-Then, you see the following in the terminal:
-
-```bash
- LockCreator Tests
-Creating
-Balance 10000000000000000
-locks length 1
-Locks index 0
-Locks index 1
-1) LockCreator Tests
- should get all locks:
- Error: VM Exception while processing transaction: reverted with panic code 0x32 (Array accessed at an out-of-bounds or negative index)
-```
-
-Since arrays are 0 index based, an array with 1 item will store that item at the 0 index. In the above example, the `if` statement compares `<=` against the length of the array, so it tries to access the element in position 1, and crashes.
-
-Here's the simple solution:
-
-```solidity
- function getAllLocks() external view returns(Lock[] memory result) {
- result = new Lock[](locks.length);
-
- console.log("locks length %s", locks.length);
-
- for(uint i = 0; i < locks.length; i++){
- console.log("Locks index %s", i);
- result[i] = locks[i];
- }
-}
-```
-
-Which immediately solves the problem:
-
-```bash
- LockCreator Tests
-Creating
-Balance 10000000000000000
-locks length 1
-Locks index 0
-all locks Result(1) [ '0x83BA8C2028EE8a6476396145C7692fBD09337acD' ]
- ✔ should get all locks
-
- 1 passing (3s)
-```
-
-## Conclusion
-
-In this tutorial, you've learned some techniques about how to debug smart contracts using Hardhat. You explored some common cases of various errors and how by simply using `console.log` and a proper test, you can identify and solve the problem.
-
-## See also
-
-[Console.log]: https://hardhat.org/hardhat-network/docs/reference#console.log
-[Error Triage]: https://docs.base.org/learn/error-triage/error-triage
-[Base Learn]: https://base.org/learn
-
diff --git a/docs/learn/hardhat/hardhat-tools-and-testing/deploy-with-hardhat.mdx b/docs/learn/hardhat/hardhat-tools-and-testing/deploy-with-hardhat.mdx
deleted file mode 100644
index e1418d4a2..000000000
--- a/docs/learn/hardhat/hardhat-tools-and-testing/deploy-with-hardhat.mdx
+++ /dev/null
@@ -1,405 +0,0 @@
----
-title: Deploying a smart contract using Hardhat
-slug: /deploy-with-hardhat
-description: "A tutorial that teaches how to deploy a smart contract on the Base test network using Hardhat. Includes instructions for setting up the environment, compiling, and deploying the smart contract."
-author: taycaldwell
----
-
-This section will guide you through deploying an NFT smart contract (ERC-721) on the Base test network using [Hardhat](https://hardhat.org/).
-
-Hardhat is a developer tool that provides a simple way to deploy, test, and debug smart contracts.
-
-
-
-## Objectives
-
-By the end of this tutorial, you should be able to do the following:
-
-- Setup Hardhat for Base
-- Create an NFT smart contract for Base
-- Compile a smart contract for Base
-- Deploy a smart contract to Base
-- Interact with a smart contract deployed on Base
-
-
-
-## Prerequisites
-
-### Node v18+
-
-This tutorial requires you have Node version 18+ installed.
-
-- Download [Node v18+](https://nodejs.org/en/download/)
-
-If you are using `nvm` to manage your node versions, you can just run `nvm install 18`.
-
-### Coinbase Wallet
-
-In order to deploy a smart contract, you will first need a web3 wallet. You can create a wallet by downloading the Coinbase Wallet browser extension.
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-Deploying contracts to the blockchain requires a gas fee. Therefore, you will need to fund your wallet with ETH to cover those gas fees.
-
-For this tutorial, you will be deploying a contract to the Base Sepolia test network. You can fund your wallet with Base Sepolia ETH using one of the faucets listed on the Base [Network Faucets](https://docs.base.org/base-chain/tools/network-faucets) page.
-
-
-
-## Creating a project
-
-Before you can begin deploying smart contracts to Base, you need to set up your development environment by creating a Node.js project.
-
-To create a new Node.js project, run:
-
-```bash
-npm init --y
-```
-
-Next, you will need to install Hardhat and create a new Hardhat project
-
-To install Hardhat, run:
-
-```bash
-npm install --save-dev hardhat
-```
-
-To create a new Hardhat project, run:
-
-```bash
-npx hardhat init
-```
-
-Select `Create a TypeScript project` then press _enter_ to confirm the project root.
-
-Select `y` for both adding a `.gitignore` and loading the sample project. It will take a moment for the project setup process to complete.
-
-
-
-## Configuring Hardhat with Base
-
-In order to deploy smart contracts to the Base network, you will need to configure your Hardhat project and add the Base network.
-
-To configure Hardhat to use Base, add Base as a network to your project's `hardhat.config.ts` file:
-
-```tsx
-import { HardhatUserConfig } from 'hardhat/config';
-import '@nomicfoundation/hardhat-toolbox';
-
-require('dotenv').config();
-
-const config: HardhatUserConfig = {
- solidity: {
- version: '0.8.23',
- },
- networks: {
- // for mainnet
- 'base-mainnet': {
- url: 'https://mainnet.base.org',
- accounts: [process.env.WALLET_KEY as string],
- gasPrice: 1000000000,
- },
- // for testnet
- 'base-sepolia': {
- url: 'https://sepolia.base.org',
- accounts: [process.env.WALLET_KEY as string],
- gasPrice: 1000000000,
- },
- // for local dev environment
- 'base-local': {
- url: 'http://localhost:8545',
- accounts: [process.env.WALLET_KEY as string],
- gasPrice: 1000000000,
- },
- },
- defaultNetwork: 'hardhat',
-};
-
-export default config;
-```
-
-### Install Hardhat toolbox
-
-The above configuration uses the `@nomicfoundation/hardhat-toolbox` plugin to bundle all the commonly used packages and Hardhat plugins recommended to start developing with Hardhat.
-
-To install `@nomicfoundation/hardhat-toolbox`, run:
-
-```bash
-npm install --save-dev @nomicfoundation/hardhat-toolbox
-```
-
-### Loading environment variables
-
-The above configuration also uses [dotenv](https://www.npmjs.com/package/dotenv) to load the `WALLET_KEY` environment variable from a `.env` file to `process.env.WALLET_KEY`. You should use a similar method to avoid hardcoding your private keys within your source code.
-
-To install `dotenv`, run:
-
-```bash
-npm install --save-dev dotenv
-```
-
-Once you have `dotenv` installed, you can create a `.env` file with the following content:
-
-```
-WALLET_KEY=""
-```
-
-Substituting `` with the private key for your wallet.
-
-
-
-`WALLET_KEY` is the private key of the wallet to use when deploying a contract. For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key). **It is critical that you do NOT commit this to a public repo**
-
-
-
-### Local Networks
-
-You can run the Base network locally, and deploy using it. If this is what you are looking to do, see the [repo containing the relevant Docker builds](https://github.com/base-org/node).
-
-It will take a **very** long time for your node to sync with the network. If you get errors that the `nonce has already been used` when trying to deploy, you aren't synced yet.
-
-For quick testing, such as if you want to add unit tests to the below NFT contract, you may wish to leave the `defaultNetwork` as `'hardhat'`.
-
-
-
-## Compiling the smart contract
-
-Below is a simple NFT smart contract (ERC-721) written in the Solidity programming language:
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.23;
-
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
-
-contract NFT is ERC721 {
- uint256 public currentTokenId;
-
- constructor() ERC721("NFT Name", "NFT") {}
-
- function mint(address recipient) public payable returns (uint256) {
- uint256 newItemId = ++currentTokenId;
- _safeMint(recipient, newItemId);
- return newItemId;
- }
-}
-```
-
-The Solidity code above defines a smart contract named `NFT`. The code uses the `ERC721` interface provided by the [OpenZeppelin Contracts library](https://docs.openzeppelin.com/contracts/5.x/) to create an NFT smart contract. OpenZeppelin allows developers to leverage battle-tested smart contract implementations that adhere to official ERC standards.
-
-To add the OpenZeppelin Contracts library to your project, run:
-
-```bash
-npm install --save @openzeppelin/contracts
-```
-
-In your project, delete the `contracts/Lock.sol` contract that was generated with the project and add the above code in a new file called `contracts/NFT.sol`. (You can also delete the `test/Lock.ts` test file, but you should add your own tests ASAP!).
-
-To compile the contract using Hardhat, run:
-
-```bash
-npx hardhat compile
-```
-
-
-
-## Deploying the smart contract
-
-Once your contract has been successfully compiled, you can deploy the contract to the Base Sepolia test network.
-
-To deploy the contract to the Base Sepolia test network, you'll need to modify the `scripts/deploy.ts` in your project:
-
-```tsx
-import { ethers } from 'hardhat';
-
-async function main() {
- const nft = await ethers.deployContract('NFT');
-
- await nft.waitForDeployment();
-
- console.log('NFT Contract Deployed at ' + nft.target);
-}
-
-// We recommend this pattern to be able to use async/await everywhere
-// and properly handle errors.
-main().catch((error) => {
- console.error(error);
- process.exitCode = 1;
-});
-```
-
-You'll also need testnet ETH in your wallet. See the [prerequisites](#prerequisites) if you haven't done that yet. Otherwise, the deployment attempt will fail.
-
-Finally, run:
-
-```bash
-npx hardhat run scripts/deploy.ts --network base-sepolia
-```
-
-The contract will be deployed on the Base Sepolia test network. You can view the deployment status and contract by using a [block explorer](/base-chain/tools/block-explorers) and searching for the address returned by your deploy script. If you've deployed an exact copy of the NFT contract above, it will already be verified and you'll be able to read and write to the contract using the web interface.
-
-
-
-If you'd like to deploy to mainnet, you'll modify the command like so:
-
-```bash
-npx hardhat run scripts/deploy.ts --network base-mainnet
-```
-
-
-
-Regardless of the network you're deploying to, if you're deploying a new or modified contract, you'll need to verify it first.
-
-
-
-## Verifying the Smart Contract
-
-If you want to interact with your contract on the block explorer, you, or someone, needs to verify it first. The above contract has already been verified, so you should be able to view your version on a block explorer already. For the remainder of this tutorial, we'll walk through how to verify your contract on Base Sepolia testnet.
-
-In `hardhat.config.ts`, configure Base Sepolia as a custom network. Add the following to your `HardhatUserConfig`:
-
-
-```tsx
-etherscan: {
- apiKey: {
- "base-sepolia": "PLACEHOLDER_STRING"
- },
- customChains: [
- {
- network: "base-sepolia",
- chainId: 84532,
- urls: {
- apiURL: "https://api-sepolia.basescan.org/api",
- browserURL: "https://sepolia.basescan.org"
- }
- }
- ]
- },
-```
-
-
-
-You can get your Basescan API key from [basescan.org](https://basescan.org/myapikey) when you sign up for an account.
-
-
-
-Now, you can verify your contract. Grab the deployed address and run:
-
-```bash
-npx hardhat verify --network base-sepolia
-```
-
-You should see an output similar to:
-
-
-```
-Nothing to compile
-No need to generate any newer typings.
-Successfully submitted source code for contract
-contracts/NFT.sol:NFT at 0x6527E5052de5521fE370AE5ec0aFCC6cD5a221de
-for verification on the block explorer. Waiting for verification result...
-
-Successfully verified contract NFT on Etherscan.
-```
-
-
-
- ```tsx
- etherscan: {
- apiKey: {
- "base-sepolia": "PLACEHOLDER_STRING"
- },
- customChains: [
- {
- network: "base-sepolia",
- chainId: 84532,
- urls: {
- apiURL: "https://api-sepolia.basescan.org/api",
- browserURL: "https://sepolia.basescan.org"
- }
- }
- ]
- },
- ```
-
- You can get your Basescan API key from [basescan.org](https://basescan.org/myapikey) when you sign up for an account.
-
-
- Now, you can verify your contract. Grab the deployed address and run:
-
- ```bash
- npx hardhat verify --network base-sepolia
- ```
-
- You should see an output similar to:
-
- ```
- Nothing to compile
- No need to generate any newer typings.
- Successfully submitted source code for contract
- contracts/NFT.sol:NFT at 0x6527E5052de5521fE370AE5ec0aFCC6cD5a221de
- for verification on the block explorer. Waiting for verification result...
-
- Successfully verified contract NFT on Etherscan.
- ```
-
-
- ```tsx
- // Hardhat expects etherscan here, even if you're using Blockscout.
- etherscan: {
- apiKey: {
- "base-sepolia": process.env.BLOCKSCOUT_KEY as string
- },
- customChains: [
- {
- network: "base-sepolia",
- chainId: 84532,
- urls: {
- apiURL: "https://base-sepolia.blockscout.com/api",
- browserURL: "https://base-sepolia.blockscout.com"
- }
- }
- ]
- },
- ```
-
- You can get your Blockscout API key from [here](https://base-sepolia.blockscout.com/account/api_key) after you sign up for an account.
-
-
- Now, you can verify your contract. Grab the deployed address and run:
-
- ```bash
- npx hardhat verify --network base-sepolia
- ```
-
- You should see an output similar to:
-
- ```
- Nothing to compile
- No need to generate any newer typings.
- Successfully submitted source code for contract
- contracts/NFT.sol:NFT at 0x6527E5052de5521fE370AE5ec0aFCC6cD5a221de
- for verification on the block explorer. Waiting for verification result...
-
- Successfully verified contract NFT on Etherscan.
- ```
-
-
-
-You can't re-verify a contract identical to one that has already been verified. If you attempt to do so, such as verifying the above contract, you'll get an error similar to:
-
-```text
-Error in plugin @nomiclabs/hardhat-etherscan: The API responded with an unexpected message.
-Contract verification may have succeeded and should be checked manually.
-Message: Already Verified
-```
-
-
-Search for your contract on [Blockscout](https://base-sepolia.blockscout.com/) or [Basescan](https://sepolia.basescan.org/) to confirm it is verified.
-
-## Interacting with the Smart Contract
-
-If you verified on Basescan, you can use the `Read Contract` and `Write Contract` tabs to interact with the deployed contract. You'll need to connect your wallet first, by clicking the Connect button.
-
-
diff --git a/docs/learn/hardhat/hardhat-tools-and-testing/optimizing-gas-usage.mdx b/docs/learn/hardhat/hardhat-tools-and-testing/optimizing-gas-usage.mdx
deleted file mode 100644
index ce17cfef2..000000000
--- a/docs/learn/hardhat/hardhat-tools-and-testing/optimizing-gas-usage.mdx
+++ /dev/null
@@ -1,423 +0,0 @@
----
-title: 'Hardhat: Optimizing the gas usage of smart contracts'
-slug: /hardhat-profiling-gas
-description: A tutorial that teaches how to optimize the gas usage of your smart contracts using Hardhat.
-author: Edson Alcala and Brian Doyle
----
-
-# Hardhat: Optimizing the gas usage of smart contracts
-
-In this tutorial, you'll learn how to profile and optimize your smart contract's gas usage with Hardhat and the [Hardhat Gas Reporter] plugin.
-
-
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Use the Hardhat Gas Reporter plugin to profile gas usage
-- Describe common strategies for improving the gas usage of a contract
-
-
-
-## Overview
-
-In the world of smart contract development, optimizing the gas consumption of your smart contracts is important. Smaller contracts consume fewer gas resources during deployment and execution, resulting in significant cost savings for your users. In this tutorial, you will leverage the Hardhat Gas Reporter plugin to help you analyze and optimize your smart contract's gas usage.
-
-The following provides further information about smart contract profiling and gas optimization.
-
-## Setting up the Hardhat Gas Reporter plugin
-
-
-
-The Hardhat Gas Reporter plugin is an invaluable tool for profiling gas usage in your smart contracts. It allows you to gain insights into the gas consumption of various contract functions, making it easier to identify potential optimization opportunities. This tool is particularly useful during development when you want to ensure your contracts are as gas-efficient as possible.
-
-To install, run `npm install -D hardhat-gas-reporter`.
-
-Then, import `hardhat-gas-reporter` in `hardhat.config.ts`:
-
-```solidity
-import "hardhat-gas-reporter"
-```
-
-Configure the plugin in the `hardhat.config.ts` file:
-
-```tsx
-const config: HardhatUserConfig = {
- // ....
- gasReporter: {
- enabled: true,
- },
-};
-```
-
-When finished, you are ready to use the plugin.
-
-## Your first gas profiling
-
-Create a contract called `Store` with the following settings:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Store {
- address public owner;
- uint256 public numberOfItems;
-
- struct Item {
- uint256 id;
- string description;
- uint256 price;
- }
-
- // id => item
- mapping(uint256 => Item) public items;
-
- constructor() {
- owner = msg.sender;
- }
-
- function addItem(string memory description, uint256 price) external {
- require(msg.sender == owner, "invalid owner");
-
- numberOfItems++;
-
- items[numberOfItems] = Item(
- numberOfItems,
- description,
- price
- );
- }
-}
-
-```
-
-Add a test file called `Store.test.ts` in order to test the gas reporter plugin. The test file should contain the following:
-
-```tsx
-import { expect } from 'chai';
-import { ethers } from 'hardhat';
-import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';
-
-import { Store, Store__factory } from '../typechain-types';
-
-describe('Store tests', function () {
- let instance: Store;
- let owner: HardhatEthersSigner;
-
- before(async () => {
- const signers = await ethers.getSigners();
- owner = signers[0];
- instance = await new Store__factory().connect(owner).deploy();
- });
-
- it('should add an item', async () => {
- const description = 'TShirt';
- const price = ethers.parseEther('1');
-
- await instance.addItem(description, price);
-
- expect(await instance.numberOfItems()).to.equal(1);
- });
-});
-```
-
-Run `npx hardhat test`. The following report appears:
-
-```
-·------------------------|---------------------------|---------------|-----------------------------·
-| Solc version: 0.8.18 · Optimizer enabled: true · Runs: 10000 · Block limit: 30000000 gas │
-·························|···························|···············|······························
-| Methods │
-·············|···········|·············|·············|···············|···············|··············
-| Contract · Method · Min · Max · Avg · # calls · usd (avg) │
-·············|···········|·············|·············|···············|···············|··············
-| Store · addItem · - · - · 113601 · 1 · - │
-·············|···········|·············|·············|···············|···············|··············
-| Deployments · · % of limit · │
-·························|·············|·············|···············|···············|··············
-| Store · - · - · 428837 · 1.4 % · - │
-·------------------------|-------------|-------------|---------------|---------------|-------------·
-```
-
-The reporter provides a detailed overview of the gas costs for the function `addItem` and the deployment costs.
-
-## Common strategies to optimize contract sizes
-
-
-
-After performing the first gas profiling, you can start ideating strategies to improve the gas costs. These strategies are certainly vast and this tutorial only covers some basic examples.
-
-### Using the optimizer
-
-From the previous report, you can identify that the optimizer of the project has a value of 10000 runs. This means the deployment costs will be more expensive. However, if you modify that value to 200, you get:
-
-```
-·------------------------|---------------------------|-------------|-----------------------------·
-| Solc version: 0.8.18 · Optimizer enabled: true · Runs: 200 · Block limit: 30000000 gas │
-·························|···························|·············|······························
-| Methods │
-·············|···········|·············|·············|·············|···············|··············
-| Contract · Method · Min · Max · Avg · # calls · usd (avg) │
-·············|···········|·············|·············|·············|···············|··············
-| Store · addItem · - · - · 113619 · 1 · - │
-·············|···········|·············|·············|·············|···············|··············
-| Deployments · · % of limit · │
-·························|·············|·············|·············|···············|··············
-| Store · - · - · 357505 · 1.2 % · - │
-·------------------------|-------------|-------------|-------------|---------------|-------------·
-```
-
-This automatically gives you some improvements for deployment gas costs but slightly more for transaction executions.
-
-### Using immutable variables
-
-In our `Store` contract, you can identify certain variables that are only set during the creation of the contract. This means that an opportunity is possible to turn those variables into immutable, since immutable variables can still be assigned at construction time.
-
-If you modify the `Store` contract to:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Store {
- address immutable owner;
- uint256 public numberOfItems;
-
- struct Item {
- uint256 id;
- string description;
- uint256 price;
- }
-
- // id => item
- mapping(uint256 => Item) public items;
-
- constructor() {
- owner = msg.sender;
- }
-
- function addItem(string memory description, uint256 price) external {
- require(msg.sender == owner, "invalid owner");
-
- numberOfItems++;
-
- items[numberOfItems] = Item(
- numberOfItems,
- description,
- price
- );
- }
-}
-```
-
-Then, run the gas reporter. You should see:
-
-```
-·------------------------|---------------------------|-------------|-----------------------------·
-| Solc version: 0.8.18 · Optimizer enabled: true · Runs: 200 · Block limit: 30000000 gas │
-·························|···························|·············|······························
-| Methods │
-·············|···········|·············|·············|·············|···············|··············
-| Contract · Method · Min · Max · Avg · # calls · usd (avg) │
-·············|···········|·············|·············|·············|···············|··············
-| Store · addItem · - · - · 111525 · 1 · - │
-·············|···········|·············|·············|·············|···············|··············
-| Deployments · · % of limit · │
-·························|·············|·············|·············|···············|··············
-| Store · - · - · 329580 · 1.1 % · - │
-·------------------------|-------------|-------------|-------------|---------------|-------------·
-```
-
-Which already presents some improvements.
-
-### Avoid unnecessary data storage
-
-Storing data and not storing data in a smart contract is a design decision that has pros and cons. Some of the pros are certainly that all the information is stored in the smart contract and you don't necessarily need to rely on events or any other service to access the storage of a contract. However, the cons of storing all the information on the contract is the fact that it will be more expensive to perform actions against the smart contract.
-
-In the `Store` smart contract, you have the following:
-
-```solidity
-struct Item {
- uint256 id;
- string description;
- uint256 price;
- }
-
-// id => item
-mapping(uint256 => Item) public items;
-```
-
-Looking closely, you can see that the `Id` of the `Item` struct and the `id` used in the mapping are similar. You can avoid duplicating this information by removing the id of the `Item` struct.
-
-The contract looks like:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Store {
- address immutable owner;
- uint256 public numberOfItems;
-
- struct Item {
- string description;
- uint256 price;
- }
-
- // id => item
- mapping(uint256 => Item) public items;
-
- constructor() {
- owner = msg.sender;
- }
-
- function addItem(string memory description, uint256 price) external {
- require(msg.sender == owner, "invalid owner");
-
- numberOfItems++;
-
- items[numberOfItems] = Item(
- description,
- price
- );
- }
-}
-```
-
-If you run the gas reporter, you then get:
-
-```
-·------------------------|---------------------------|-------------|-----------------------------·
-| Solc version: 0.8.18 · Optimizer enabled: true · Runs: 200 · Block limit: 30000000 gas │
-·························|···························|·············|······························
-| Methods │
-·············|···········|·············|·············|·············|···············|··············
-| Contract · Method · Min · Max · Avg · # calls · usd (avg) │
-·············|···········|·············|·············|·············|···············|··············
-| Store · addItem · - · - · 89371 · 1 · - │
-·············|···········|·············|·············|·············|···············|··············
-| Deployments · · % of limit · │
-·························|·············|·············|·············|···············|··············
-| Store · - · - · 322251 · 1.1 % · - │
-·------------------------|-------------|-------------|-------------|---------------|-------------·
-```
-
-This presents another improvement in the gas consumption of the `Store` smart contract. However, you can go further and instead of storing the items in a `mapping`, you can simply emit `events` and use the events as a cheap form of storage.
-
-For instance, you can modify the contract to look like:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Store {
- address immutable owner;
- uint256 public numberOfItems;
-
- struct Item {
- string description;
- uint256 price;
- }
-
- event ItemCreated(uint256 id, Item item);
-
- constructor() {
- owner = msg.sender;
- }
-
- function addItem(string memory description, uint256 price) external {
- require(msg.sender == owner, "invalid owner");
-
- numberOfItems++;
-
- emit ItemCreated(numberOfItems, Item(description, price));
- }
-}
-```
-
-Notice how instead of storing the items, you emit an `ItemCreated` event, which reduces the gas costs for deployment and execution:
-
-```
-·------------------------|---------------------------|-------------|-----------------------------·
-| Solc version: 0.8.18 · Optimizer enabled: true · Runs: 200 · Block limit: 30000000 gas │
-·························|···························|·············|······························
-| Methods │
-·············|···········|·············|·············|·············|···············|··············
-| Contract · Method · Min · Max · Avg · # calls · usd (avg) │
-·············|···········|·············|·············|·············|···············|··············
-| Store · addItem · - · - · 47315 · 1 · - │
-·············|···········|·············|·············|·············|···············|··············
-| Deployments · · % of limit · │
-·························|·············|·············|·············|···············|··············
-| Store · - · - · 208252 · 0.7 % · - │
-·------------------------|-------------|-------------|-------------|---------------|-------------·
-```
-
-As you can see, the improvements in terms of gas consumption are significant. However, the draw back is that now in order to access all of the items, you must go through all of the `ItemCreated` events emitted by the contract.
-
-### Using custom errors
-
-Another common way to optimize gas costs is by removing `require`s and use custom errors. For instance, you can do the following:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Store {
- address immutable owner;
- uint256 public numberOfItems;
-
- error InvalidOwner();
-
- struct Item {
- string description;
- uint256 price;
- }
-
- event ItemCreated(uint256 id, Item item);
-
- constructor() {
- owner = msg.sender;
- }
-
- function addItem(string memory description, uint256 price) external {
- if(msg.sender != owner){
- revert InvalidOwner();
- }
-
- numberOfItems++;
-
- emit ItemCreated(numberOfItems, Item(description, price));
- }
-}
-```
-
-Which gives you the following report:
-
-```
-·------------------------|---------------------------|-------------|-----------------------------·
-| Solc version: 0.8.18 · Optimizer enabled: true · Runs: 200 · Block limit: 30000000 gas │
-·························|···························|·············|······························
-| Methods │
-·············|···········|·············|·············|·············|···············|··············
-| Contract · Method · Min · Max · Avg · # calls · usd (avg) │
-·············|···········|·············|·············|·············|···············|··············
-| Store · addItem · - · - · 47315 · 1 · - │
-·············|···········|·············|·············|·············|···············|··············
-| Deployments · · % of limit · │
-·························|·············|·············|·············|···············|··············
-| Store · - · - · 200683 · 0.7 % · - │
-·------------------------|-------------|-------------|-------------|---------------|-------------·
-```
-
-Notice the improvement in deployment gas costs.
-
-## Conclusion
-
-In this tutorial, you've learned some common strategies to profile and optimize the gas usage of your smart contracts using the Hardhat development environment and the Hardhat Gas Reporter plugin. By implementing these strategies and leveraging the Hardhat Gas Reporter plugin, you can create more efficient and cost-effective smart contracts for the benefit of the users, since this means less gas costs.
-
----
-
-[Hardhat Gas Reporter]: https://www.npmjs.com/package/hardhat-gas-reporter
diff --git a/docs/learn/hardhat/hardhat-tools-and-testing/overview.mdx b/docs/learn/hardhat/hardhat-tools-and-testing/overview.mdx
deleted file mode 100644
index 3c649f947..000000000
--- a/docs/learn/hardhat/hardhat-tools-and-testing/overview.mdx
+++ /dev/null
@@ -1,74 +0,0 @@
----
-title: 'Overview'
-slug: /hardhat-tools-and-testing/overview
-description: What's in this learning material.
-author: Brian Doyle
-keywords:
- [
- Hardhat Tools,
- Smart Contract Development,
- Gas Optimization,
- Debugging,
- Test Coverage,
- Contract Size,
- Solidity,
- Base network,
- Base blockchain,
- blockchain development,
- ]
-hide_table_of_contents: false
-displayed_sidebar: null
----
-
-# Overview of Hardhat Tools and Testing
-
-This series of guides shows you how to use a number of [Hardhat plugins] that will help you more effectively build and test your smart contracts.
-
-Learn how to keep your contracts under the 24 kiB limit, improve gas costs for your users, make sure your unit tests fully cover your code, and practice debugging.
-
----
-
-## Objectives
-
-By the end of these guides, you should be able to:
-
-### Profiling Size
-
-- Use Hardhat Contract Sizer plugin to profile contract size
-- Describe common strategies for managing the contract size limit
-- Describe the impact that inheritance has on the byte code size limit
-- Describe the impact that external contracts have on the byte code size limit
-- Describe the impact of using libraries has on the byte code size limit
-- Describe the impact of using the Solidity optimizer
-
-### Profiling Gas
-
-- Use the Hardhat Gas Reporter plugin to profile gas usage
-- Describe common strategies for improving the gas usage of a contract
-
-### Debugging
-
-- Use `console.log` to write debugging logs
-- List common errors and their resolutions
-- Determine if an error is a contract error or an error in the test
-
-### Test Coverage
-
-- Use the Solidity Coverage plugin to analyze the coverage of your test suite
-- Increase the coverage of your test suite
-
----
-
-## Prerequisites
-
-### 1. Basic understanding of writing smart contracts
-
-These guides assume that you're reasonably comfortable writing basic smart contracts. If you're just getting started, jump over to our [Base Learn] guides and start learning!
-
-### 2. Familiarity with Hardhat
-
-We also assume that you've got Hardhat up and running, and can write unit tests for your smart contracts. If you're not there yet, but already know Solidity, you can [setup Hardhat here].
-
-[setup Hardhat here]: https://docs.base.org/base-learn/docs/hardhat-setup-overview/hardhat-setup-overview-sbs
-[Hardhat plugins]: https://hardhat.org/hardhat-runner/plugins
-[Base Learn]: https://base.org/learn
diff --git a/docs/learn/hardhat/hardhat-tools-and-testing/reducing-contract-size.mdx b/docs/learn/hardhat/hardhat-tools-and-testing/reducing-contract-size.mdx
deleted file mode 100644
index 876e0447b..000000000
--- a/docs/learn/hardhat/hardhat-tools-and-testing/reducing-contract-size.mdx
+++ /dev/null
@@ -1,502 +0,0 @@
----
-title: 'Hardhat: Optimizing the size of smart contracts'
-slug: /hardhat-profiling-size
-description: A tutorial that teaches how to optimize the size of your smart contracts using Hardhat.
-author: Edson Alcala and Brian Doyle
-keywords:
- [
- Smart Contract Sizes,
- Hardhat Contract Sizer,
- Base network,
- Base blockchain,
- Blockchain,
- Contract Optimization,
- Inheritance,
- External Contracts,
- Solidity Optimizer,
- Smart Contract Development,
- ]
-tags: ['smart contracts']
-difficulty: beginner
-hide_table_of_contents: false
-displayed_sidebar: null
----
-
-# Hardhat: Optimizing the size of smart contracts
-
-In this tutorial, you'll learn how to profile and optimize smart contract sizes with Hardhat and the [Hardhat Contract Sizer] plugin.
-
-
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Use Hardhat Contract Sizer plugin to profile contract size
-- Describe common strategies for managing the contract size limit
-- Describe the impact that inheritance has on the byte code size limit
-- Describe the impact that external contracts have on the byte code size limit
-- Describe the impact of using libraries has on the byte code size limit
-- Describe the impact of using the Solidity optimizer
-
-
-
-## Overview
-
-In the world of blockchain and Ethereum, optimizing smart contract sizes is crucial. Smaller contracts consume less gas during deployment and execution, which is translated into gas costs savings for your users. Fortunately, you can use in Hardhat the `hardhat-contract-sizer` plugin that helps you analyze and optimize the size of your smart contracts.
-
-## Setting up the Hardhat Contract Sizer plugin
-
-
-
-Hardhat Contract Sizer is a community-developed plugin that enables the profiling of smart contract by printing the size of your smart contracts in the terminal. This is helpful during development since it allows you to immediately identify potential issues with the size of your smart contracts. Keep in mind that the [maximum size of a smart contract in Ethereum] is 24 KiB.
-
-To install, run `npm install -D hardhat-contract-sizer`.
-
-Then, import `hardhat-contract-sizer` in `hardhat.config.ts`:
-
-```solidity
-import "hardhat-contract-sizer"
-```
-
-When finished, you are ready to use the plugin.
-
-## Your first size profiling
-
-Similar to the previous tutorials, you begin by profiling the smart contract `Lock.sol`.
-
-Run `npx hardhat size-contracts`, which is a task added to Hardhat once you set up and configure the `hardhat-contract-sizer` plugin.
-
-You are then able to see:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: false · Runs: 200 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | BalanceReader · 0.612 () · 0.644 () │
- ·························|································|·································
- | Lock · 1.009 () · 1.461 () │
-```
-
-Although your contract is simple, you can see immediately the power of the `hardhat-contract-sizer` plugin, since it show you the size of your contracts.
-
-## Common strategies to optimize contract sizes
-
-
-
-In order to illustrate some of the strategies to optimize the size of your contracts, create two smart contracts, `Calculator.sol` and `ScientificCalculator.sol`, with the following:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Calculator {
- function add(uint256 a, uint256 b) external pure returns(uint256) {
- require(a > 0 && b > 0, "Invalid values");
- return a + b;
- }
-
- function sub(uint256 a, uint256 b) external pure returns(uint256) {
- require(a > 0 && b > 0, "Invalid values");
- return a - b;
- }
-
- function mul(uint256 a, uint256 b) external pure returns(uint256) {
- require(a > 0 && b > 0, "Invalid values");
- return a * b;
- }
-
- function div(uint256 a, uint256 b) external pure returns(uint256) {
- require(a > 0 && b > 0, "Invalid values");
- return a / b;
- }
-}
-```
-
-```solidity
-contract ScientificCalculator is Calculator {
- function power(uint256 base, uint256 exponent) public pure returns (uint256) {
- require(base > 0 && exponent > 0, "Invalid values");
-
- return base ** exponent;
- }
-}
-```
-
-Then, run the command `npx hardhat size-contracts` again and you should be able to see:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: false · Runs: 200 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | BalanceReader · 0.612 (0.000) · 0.644 (0.000) │
- ·························|································|·································
- | Lock · 1.009 (0.000) · 1.461 (0.000) │
- ·························|································|·································
- | Calculator · 1.299 () · 1.330 () │
- ·························|································|·································
- | ScientificCalculator · 1.827 () · 1.858 () │
- ·------------------------|--------------------------------|--------------------------------·
-```
-
-Notice how the size of `ScientificCalculator` is bigger than `Calculator`. This is because `ScientificCalculator` is inheriting the contract `Calculator`, which means all of its functionality and code is available in `ScientificCalculator` and that will influence its size.
-
-### Code abstraction and modifiers
-
-At this point as a smart contract developer, you can review your smart contract code and look for ways into you can optimize it.
-
-The first thing you notice in the source code is the extensive use of:
-
-```solidity
-require(a > 0 && b > 0, "Invalid values");
-```
-
-A possible optimization is to abstract repetitive code into [modifiers], such as the following:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract Calculator {
- error InvalidInput();
-
- function add(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a + b;
- }
-
- function sub(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a - b;
- }
-
- function mul(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a * b;
- }
-
- function div(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a / b;
- }
-
- modifier onlyValidInputs(uint256 a, uint256 b) {
- if(a == 0 && b == 0){
- revert InvalidInput();
- }
- _;
- }
-}
-```
-
-And for `ScientificCalculator`:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "./Calculator.sol";
-
-contract ScientificCalculator is Calculator {
- function power(uint256 base, uint256 exponent) public pure onlyValidInputs(base,exponent) returns (uint256) {
- return base ** exponent;
- }
-}
-```
-
-Notice the usage of the modifier and the replacement of the require to use a custom error.
-
-When you run the `npx hardhat size-contracts` command, you should be able to see:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: false · Runs: 200 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | BalanceReader · 0.612 (0.000) · 0.644 (0.000) │
- ·························|································|·································
- | Lock · 1.009 (0.000) · 1.461 (0.000) │
- ·························|································|·································
- | Calculator · 1.165 (0.000) · 1.196 (0.000) │
- ·························|································|·································
- | ScientificCalculator · 1.690 (0.000) · 1.722 (0.000) │
- ·------------------------|--------------------------------|--------------------------------·
-```
-
-Although the optimization is small, you can see that there are some improvements.
-
-You can continue this process until you feel comfortable with the size of the contract.
-
-### Split into multiple contracts
-
-It is common to split your smart contracts into multiple contracts, not only because of the size limitations but to create better abstractions, to improve readability, and to avoid repetition.
-
-From a contract size perspective, having multiple independent contracts will reduce the size of each contract. For example, the original size of a smart contract was 30 KiB: by splitting into 2, you will end up with 2 smart contracts of ~15 KiB that are within the limits of Solidity. Keep in mind that this will influence gas costs during the execution of the contract because it will require it to call an external contract.
-
-In order to explain this example, create a contract called `Computer` that contains a function called `executeProcess`:
-
-```tsx
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "hardhat/console.sol";
-
-contract Computer {
- function executeProcess() external view {
- // ...logic to be implemented
- }
-}
-```
-
-In this example, the `executeProcess` function of `Computer` requires certain functionality of `Calculator` and a new contract called `Printer`:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "hardhat/console.sol";
-
-contract Printer {
- function print(string memory _content) external view {
- require(bytes(_content).length > 0, "invalid length");
- console.log(_content);
- }
-}
-```
-
-The easiest way for `Computer` to access both functionalities is to inherit; however, as all of these contracts continue adding functionality, the size of the code will also increase. You will reach the contract size issue at some point, since you are copying the entire functionality into your contract. You can better allow that functionality to be kept with their specific contracts and if the `Computer` requires to access that functionality, you could call the `Calculator` and `Printer` contracts.
-
-But in this example, there is a process that must call both `Calculator` and `Printer`:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "hardhat/console.sol";
-
-import "./Calculator.sol";
-import "./Printer.sol";
-
-contract Computer {
- Calculator calculator;
- Printer printer;
-
- constructor(address _calculator, address _printer) {
- calculator = Calculator(_calculator);
- printer = Printer(_printer);
- }
-
- function executeProcess() external view {
- // call Calculator contract, i.e calculator.add(a, b);
- // call Printer contract, i.e printer.print("value to print");
- }
-}
-```
-
-If you run the contract sizer plugin, you get:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: true · Runs: 10000 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | console · 0.084 (0.000) · 0.138 (0.000) │
- ·························|································|·································
- | Computer · 0.099 (0.000) · 0.283 (0.000) │
- ·························|································|·································
- | Calculator · 0.751 (0.000) · 0.782 (0.000) │
- ·························|································|·································
- | Printer · 0.761 (0.000) · 0.792 (0.000) │
- ·························|································|·································
- | ScientificCalculator · 1.175 (0.000) · 1.206 (0.000) │
- ·------------------------|--------------------------------|--------------------------------·
-```
-
-Notice how your `Computer` contract is very small but still has the capability to access all the functionality of `Printer` and `Calculator`.
-
-Although this will reduce the size of each contract, the costs of this are discussed more deeply in the [Gas Optimization] article.
-
-### Using libraries
-
-Libraries are another common way to encapsulate and abstract common functionality that can be shared across multiple contracts. This can significantly impact the bytecode size of the smart contracts. Remember that in Solidity, libraries can be external and internal.
-
-The way internal libraries affect the contract size is very similar to the way inherited contracts affects a contract's size; this is because the internal functions of the library is included within the final bytecode.
-
-But when the libraries are external, the behavior is different: the way Solidity calls external libraries is by using a special function called [delegate call].
-
-External libraries are commonly deployed independently and can be reused my multiple contracts. Since libraries don't keep a state, they behave like pure functions in the Blockchain.
-
-In this example, your computer will use the `Calculator` library only. Then, you would have the following:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-library Calculator {
- error InvalidInput();
-
- function add(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a + b;
- }
-
- function sub(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a - b;
- }
-
- function mul(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a * b;
- }
-
- function div(uint256 a, uint256 b) external pure onlyValidInputs(a,b) returns(uint256) {
- return a / b;
- }
-
- modifier onlyValidInputs(uint256 a, uint256 b) {
- if(a == 0 && b == 0){
- revert InvalidInput();
- }
- _;
- }
-}
-```
-
-Then, `Computer` is:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "hardhat/console.sol";
-
-import "./Calculator.sol";
-import "./Printer.sol";
-
-contract Computer {
- using Calculator for uint256;
-
- function executeProcess() external view {
- uint256 a = 1;
- uint256 b = 2;
- uint256 result = a.add(b);
- // ... logic to be implemented
- }
-}
-```
-
-Notice how you instructing the smart contract to use the `Calculator` library for `uint256` and how in the `executeProcess` function, you can now use the `add` function from the `Calculator` library in all of the `uint256`.
-
-If you run the `npx hardhat size-contracts` command, you then get:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: true · Runs: 10000 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | Calculator · 0.761 · 0.817 │
- ·························|································|·································
- | Printer · 0.771 · 0.827 │
- ·························|································|·································
- | Computer · 0.961 · 0.992 │
- ·------------------------|--------------------------------|--------------------------------·
-```
-
-In order to compare the impact, you can modify the external modifier from all of the `Calculator` library functions and you will then have:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: true · Runs: 10000 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | Calculator · 0.084 · 0.138 │
- ·························|································|·································
- | Printer · 0.084 · 0.138 │
- ·························|································|·································
- | Computer · 1.139 · 1.170 │
- ·------------------------|--------------------------------|--------------------------------·
-```
-
-Which demonstrates why using external libraries can be a good option in order to optimize the size of your contracts.
-
-### Using the Solidity compiler optimizer
-
-
-
-Another way to optimize the size of the smart contracts is to simply use the Solidity optimizer.
-
-From the [Solidity official docs]:
-
-> Overall, the optimizer tries to simplify complicated expressions, which reduces both code size and execution cost.
-
-You can enable the solidity optimizer in hardhat by simply adding the following to the `hardhat.config.ts` file:
-
-```solidity
-const config: HardhatUserConfig = {
- solidity: {
- version: "0.8.18",
- settings: {
- optimizer: {
- enabled: true,
- runs: 200
- }
- }
- },
- ...
-}
-```
-
-Notice the optimizer is enabled and has a parameter `runs`. If you run the contract sizer command again, you will see the following:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: true · Runs: 200 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | BalanceReader · 0.351 (-0.262) · 0.382 (-0.262) │
- ·························|································|·································
- | Lock · 0.471 (-0.538) · 0.661 (-0.800) │
- ·························|································|·································
- | Calculator · 0.604 (-0.561) · 0.636 (-0.561) │
- ·························|································|·································
- | ScientificCalculator · 0.930 (-0.761) · 0.961 (-0.761) │
- ·------------------------|--------------------------------|--------------------------------·
-```
-
-Notice the bigger improvement, but see what happens if you increase the `runs` parameter value to 1000:
-
-```
- ·------------------------|--------------------------------|--------------------------------·
- | Solc version: 0.8.18 · Optimizer enabled: true · Runs: 1000 │
- ·························|································|·································
- | Contract Name · Deployed size (KiB) (change) · Initcode size (KiB) (change) │
- ·························|································|·································
- | BalanceReader · 0.400 (+0.050) · 0.432 (+0.050) │
- ·························|································|·································
- | Lock · 0.537 (+0.066) · 0.728 (+0.066) │
- ·························|································|·································
- | Calculator · 0.604 (0.000) · 0.636 (0.000) │
- ·························|································|·································
- | ScientificCalculator · 0.945 (+0.016) · 0.977 (+0.016) │
- ·------------------------|--------------------------------|--------------------------------·
-```
-
-The size of the contract increased, however this means your code will be more efficient across the lifetime of the contract because the higher the `runs` value the more efficient during execution but more expensive during deployment. You can read more in the [Solidity documentation].
-
-## Conclusion
-
-In this tutorial, you've learned how to profile and optimise smart contracts using the Hardhat development environment and the Hardhat Contract Sizer plugin. By focusing on the critical aspect of contract size, we've equipped ourselves with tools and strategies to create more efficient Solidity code.
-
-As you continue your journey in smart contract development, keep in mind that optimizing contract sizes is a continuous process that requires careful consideration of trade-offs between size, readability, and gas efficiency.
-
-
-[Hardhat Contract Sizer]: https://github.com/ItsNickBarry/hardhat-contract-sizer
-[maximum size of a smart contract in Ethereum]: https://ethereum.org/en/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/#why-is-there-a-limit
-[modifiers]: https://docs.base.org/learn/advanced-functions/function-modifiers
-[Solidity official docs]: https://docs.soliditylang.org/en/v0.8.20/internals/optimizer.html
-[Delegate call]: https://solidity-by-example.org/delegatecall/
-[Gas Optimization]: ./hardhat-profiling-gas
-[Solidity documentation]: https://docs.soliditylang.org/en/v0.8.20/internals/optimizer.html#optimizer-parameter-runs
diff --git a/docs/learn/hardhat/hardhat-verify/hardhat-verify-sbs.mdx b/docs/learn/hardhat/hardhat-verify/hardhat-verify-sbs.mdx
deleted file mode 100644
index bc9f5b70e..000000000
--- a/docs/learn/hardhat/hardhat-verify/hardhat-verify-sbs.mdx
+++ /dev/null
@@ -1,117 +0,0 @@
----
-title: Verifying Smart Contracts
-description: Verifying smart contracts with Hardhat.
-hide_table_of_contents: false
----
-
-# Verifying Smart Contracts
-
-In this article, you'll learn how to verify smart contracts in Etherscan with hardhat and the hardhat deploy plugin.
-
----
-
-## Objectives
-
-By the end of this lesson, you should be able to:
-
-- Verify a deployed smart contract on Etherscan
-- Connect a wallet to a contract in Etherscan
-- Use etherscan to interact with your own deployed contract
-
----
-
-## Overview
-
-Verifying smart contracts plays an important role in providing security and certainty to the users of your decentralized applications. By offering full visibility of the source code of your smart contract, you provide confidence and transparency of the intention of the code that is being executed.
-
-The way smart contracts are verified is by simply uploading the source code and contract address to services such as Etherscan.
-
-Once the contract is verified, the Etherscan explorer shows a status like the following image:
-
-
-
-
-
-Luckily, Hardhat and Hardhat-deploy already contain a built-in capability to do this task easily on your behalf.
-
-This process involves the following steps:
-
-1. Getting an Etherscan key
-2. Configuring Hardhat
-3. Verifying
-
-## Getting an Etherscan key
-
-In order to obtain an Etherscan API key, visit [Etherscan](https://etherscan.io/) and create an account.
-
-Then, go to [https://etherscan.io/myapikey](https://etherscan.io/myapikey) and create an API key by clicking the **Add** button:
-
-
-
-
-
-Bear in mind that different networks have other Blockchain explorers. For example:
-
-- [Base](https://basescan.org/)
-- [Sepolia](https://sepolia.etherscan.io/)
-
-You'll need to go to that particular explorer and get the API Key following a similar process as mentioned previously (except for Sepolia Etherscan, where you can use the Etherscan mainnet one instead).
-
-## Configuring Hardhat
-
-You can configure the Etherscan API Key for each different network. For example, include the following to the `hardhat.config.ts` file for Base Sepolia:
-
-```tsx
-base_sepolia: {
- url: "https://sepolia.base.org",
- accounts: {
- mnemonic: process.env.MNEMONIC ?? ""
- },
- verify: {
- etherscan: {
- apiUrl: "https://api-sepolia.basescan.org",
- apiKey: process.env.ETHERSCAN_API_KEY
- }
- }
-}
-```
-
-Include in your `.env` file the following:
-
-```
-ETHERSCAN_API_KEY=
-```
-
-## Verifying
-
-You verify in base, and to do so, simply run the following command:
-
-```bash
-npx hardhat --network base_sepolia etherscan-verify
-```
-
-You should receive the following response:
-
-```
-verifying Lock ...
-waiting for result...
- => contract Lock is now verified
-```
-
-You can now go to Basescan and search for your contract address, where you'll see the following:
-
-
-
-
-
-## Conclusion
-
-In this lesson, you've learned how to verify smart contracts using Hardhat and Hardhat-deploy. You learned how to configure Hardhat to support multiple networks and verify by using a simple command.
-
----
-
-## See also
-
-[Solidity Docs]: https://docs.soliditylang.org/en/v0.8.17/
-[Remix Project]: https://remix-project.org/
-[Hardhat Deploy]: https://github.com/wighawag/hardhat-deploy
diff --git a/docs/learn/hardhat/hardhat-verify/hardhat-verify-vid.mdx b/docs/learn/hardhat/hardhat-verify/hardhat-verify-vid.mdx
deleted file mode 100644
index 0464866bf..000000000
--- a/docs/learn/hardhat/hardhat-verify/hardhat-verify-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Verifying Smart Contracts
-description: Verify your contracts with Hardhat.
-hide_table_of_contents: false
----
-
-# Verifying Smart Contracts
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/imports/imports-exercise.mdx b/docs/learn/imports/imports-exercise.mdx
deleted file mode 100644
index dc44a87b1..000000000
--- a/docs/learn/imports/imports-exercise.mdx
+++ /dev/null
@@ -1,91 +0,0 @@
----
-title: Imports Exercise
-description: Exercise - Demonstrate your knowledge of imports.
-sidebarTitle: Exercise
-hide_table_of_contents: false
----
-
-
-Create a contract that adheres to the following specifications.
-
----
-
-## Contract
-
-Create a contract called `ImportsExercise`. It should `import` a copy of `SillyStringUtils`
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-library SillyStringUtils {
-
- struct Haiku {
- string line1;
- string line2;
- string line3;
- }
-
- function shruggie(string memory _input) internal pure returns (string memory) {
- return string.concat(_input, unicode" 🤷");
- }
-}
-```
-
-Add a public instance of `Haiku` called `haiku`.
-
-Add the following two functions.
-
-### Save Haiku
-
-`saveHaiku` should accept three strings and save them as the lines of `haiku`.
-
-### Get Haiku
-
-`getHaiku` should return the haiku as a `Haiku` type.
-
-
-Remember, the compiler will automatically create a getter for `public` `struct`s, but these return each member individually. Create your own getters to return the type.
-
-
-
-### Shruggie Haiku
-
-`shruggieHaiku` should use the library to add 🤷 to the end of `line3`. It must **not** modify the original haiku. It should return the modified `Haiku`.
-
----
-
-## Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Contract Verification Best Practices
-
-To simplify the verification of your contract on a blockchain explorer like BaseScan.org, consider these two common strategies:
-
-1. **Flattening**: This method involves combining your main contract and all of its imported dependencies into a single file. This makes it easier for explorers to verify the code since they only have to process one file.
-
-2. **Modular Deployment**: Alternatively, you can deploy each imported contract separately and then reference them in your main contract via their deployed addresses. This approach maintains the modularity and readability of your code. Each contract is deployed and verified independently, which can facilitate easier updates and reusability.
-
-3. **Use Desktop Tools**: Forge and Hardhat both have tools to write scripts that both deploy and verify your contracts.
-
-
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/imports/imports-sbs.mdx b/docs/learn/imports/imports-sbs.mdx
deleted file mode 100644
index af05a4441..000000000
--- a/docs/learn/imports/imports-sbs.mdx
+++ /dev/null
@@ -1,98 +0,0 @@
----
-title: Imports
-sidebarTitle: Step by Step Guide
-description: Learn to import code into your contract.
-hide_table_of_contents: false
----
-
-In this lesson, we'll learn how to import code written by others into your contracts. We'll also explore the [OpenZeppelin] library of smart contracts.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Import and use code from another file
-- Utilize OpenZeppelin contracts within Remix
-
----
-
-## OpenZeppelin
-
-[OpenZeppelin] has a robust [library] of well-[documented] smart contracts. These include a number of standard-compliant token implementations and a suite of utilities. All the contracts are audited and are therefore safer to use than random code you might find on the internet (you should still do your own audits before releasing to production).
-
-### Docs
-
-The [docs] start with installation instructions, which we'll return to when we switch over to local development. You do **not** need to install anything to use these contracts in Remix.
-
-Find the documentation for the `EnumerableSet` under _Utils_. This library will allow you to create [sets] of `bytes32`, `address`, and `uint256`. Since they're enumerated, you can iterate through them. Neat!
-
-### Implementing the OpenZeppelin EnumerableSet
-
-Create a new file to work in and add the `pragma` and license identifier.
-
-In Remix, you can import libraries directly from GitHub!
-
-```solidity
-import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol";
-```
-
-You should see `EnumerableSet.sol` pop into your workspace files, nested deeply in a bunch of folders.
-
-### Trying It Out
-
-Add a contract called `SetExploration`. Review the extensive comments within the contract itself.
-
-To use the `EnumerableSet`, you need to use the [`using`] keyword. This directive attaches all of the library methods to the type. Doing so allows you to call the method on the variable with dot notation, and the variable itself will be supplied as the first argument.
-
-Follow the pattern in the example in the comments, but name the variable `visitors`:
-
-```
-using EnumerableSet for EnumerableSet.AddressSet;
-
-EnumerableSet.AddressSet private visitors;
-```
-
-Add a function called `registerVisitor` that makes use of the library's `add` function to add the sender of the message to the `visitors` set.
-
-
-There's also an `_add` function, which is private.
-
-
-
-
-
-```solidity
-function registerVisitor() public {
- visitors.add(msg.sender);
-}
-```
-
-
-
-Add another function to return the `numberOfVisitors`. Thanks to `using`, this can cleanly call the `length` function:
-
-
-
-```solidity
-function numberOfVisitors() public view returns (uint) {
- return visitors.length();
-}
-```
-
-
----
-
-## Conclusion
-
-In this lesson, you imported a library from [OpenZeppelin] and implemented some of its functions. You also learned how to use the `using` keyword.
-
----
-
-[OpenZeppelin]: https://www.openzeppelin.com/
-[library]: https://github.com/OpenZeppelin/openzeppelin-contracts
-[documented]: https://docs.openzeppelin.com/contracts/4.x/
-[docs]: https://docs.openzeppelin.com/contracts/4.x/
-[sets]: https://en.wikipedia.org/wiki/Set_(abstract_data_type)
-[`using`]: https://docs.soliditylang.org/en/v0.8.17/contracts.html#using-for
diff --git a/docs/learn/imports/imports-vid.mdx b/docs/learn/imports/imports-vid.mdx
deleted file mode 100644
index 11e176558..000000000
--- a/docs/learn/imports/imports-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Imports
-sidebarTitle: Imports Overview
-description: Import libraries and contracts into your own contracts.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/inheritance/abstract-contracts-sbs.mdx b/docs/learn/inheritance/abstract-contracts-sbs.mdx
deleted file mode 100644
index 70d61de8e..000000000
--- a/docs/learn/inheritance/abstract-contracts-sbs.mdx
+++ /dev/null
@@ -1,77 +0,0 @@
----
-title: Abstract Contracts
-sidebarTitle: Abstract Contracts Guide
-description: Learn how to make contracts that must be inherited by another contract.
-hide_table_of_contents: false
----
-
-[Abstract] contracts can't exist on their own. Their functionality can only be utilized by a contract that inherits from them. In this lesson, you'll learn how to create an abstract contract.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Use the virtual, override, and abstract keywords to create and use an abstract contract
-
----
-
-## Abstract Contracts
-
-Continue with your `Inheritance.sol` file. Add `ContractD` as an `abstract contract`. Add a `virtual` function called `whoAreYou` function, but do **not** add any implementation for that function.
-
-
-
-```solidity
-abstract contract ContractD {
- function whoAreYou() public virtual view returns (string memory);
-}
-```
-
-
-
-### Inheriting from an Abstract Function
-
-Update `ContractA` to inherit from `ContractD`.
-
-You'll get a slightly confusing error that `ContractA` needs to be marked as `abstract`. Doing so is **not** the correct fix.
-
-```text
-from solidity:
-TypeError: Contract "ContractA" should be marked as abstract.
- --> contracts/Inheritance.sol:25:1:
- |
-25 | contract ContractA is ContractB, ContractC, ContractD {
- | ^ (Relevant source part starts here and spans across multiple lines).
-Note: Missing implementation:
- --> contracts/Inheritance.sol:6:5:
- |
-6 | function whoAreYou() public virtual view returns (string memory);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-```
-
-The clue for the correct solution is further down: `Note: Missing implementation:`
-
-Only `abstract` contracts can declare functions that are not implemented. To fix this, provide an `override` implementation for `whoAreYou` in `ContractA`:
-
-
-
-```solidity
-function whoAreYou() public override pure returns (string memory) {
- return "I'm a person!";
-}
-```
-
-
-
-
----
-
-## Conclusion
-
-In this lesson, you've learned how to implement and inherit from an abstract contract.
-
----
-
-[Abstract]: https://docs.soliditylang.org/en/v0.8.17/contracts.html?#abstract-contracts
diff --git a/docs/learn/inheritance/abstract-contracts-vid.mdx b/docs/learn/inheritance/abstract-contracts-vid.mdx
deleted file mode 100644
index b51f3100f..000000000
--- a/docs/learn/inheritance/abstract-contracts-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Abstract Contracts
-description: Create contracts that exist only to be inherited from.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/inheritance/inheritance-exercise.mdx b/docs/learn/inheritance/inheritance-exercise.mdx
deleted file mode 100644
index 1337ae94c..000000000
--- a/docs/learn/inheritance/inheritance-exercise.mdx
+++ /dev/null
@@ -1,123 +0,0 @@
----
-title: Inheritance Exercise
-description: Exercise - Demonstrate your knowledge of inheritance.
-hide_table_of_contents: false
-sidebarTitle: Exercise
----
-
-Create contracts that adhere to the following specifications.
-
----
-
-## Contracts
-
-### Employee
-
-Create an `abstract` contract called `Employee`. It should have:
-
-- A public variable storing `idNumber`
-- A public variable storing `managerId`
-- A constructor that accepts arguments for and sets both of these variables
-- A `virtual` function called `getAnnualCost` that returns a `uint`
-
-### Salaried
-
-A contract called `Salaried`. It should:
-
-- Inherit from `Employee`
-- Have a public variable for `annualSalary`
-- Implement an `override` function for `getAnnualCost` that returns `annualSalary`
-- An appropriate constructor that performs any setup, including setting `annualSalary`
-
-### Hourly
-
-Implement a contract called `Hourly`. It should:
-
-- Inherit from `Employee`
-- Have a public variable storing `hourlyRate`
-- Include any other necessary setup and implementation
-
-
-The annual cost of an hourly employee is their hourly rate \* 2080 hours.
-
-
-
-### Manager
-
-Implement a contract called `Manager`. It should:
-
-- Have a public array storing employee Ids
-- Include a function called `addReport` that can add id numbers to that array
-- Include a function called `resetReports` that can reset that array to empty
-
-### Salesperson
-
-Implement a contract called `Salesperson` that inherits from `Hourly`.
-
-### Engineering Manager
-
-Implement a contract called `EngineeringManager` that inherits from `Salaried` and `Manager`.
-
-## Deployments
-
-You'll have to do a more complicated set of deployments for this exercise.
-
-Deploy your `Salesperson` and `EngineeringManager` contracts. You don't need to separately deploy the other contracts.
-
-Use the following values:
-
-### Salesperson
-
-- Hourly rate is 20 dollars an hour
-- Id number is 55555
-- Manager Id number is 12345
-
-### Manager
-
-- Annual salary is 200,000
-- Id number is 54321
-- Manager Id is 11111
-
-## Inheritance Submission
-
-Copy the below contract and deploy it using the addresses of your `Salesperson` and `EngineeringManager` contracts.
-
-```solidity
-contract InheritanceSubmission {
- address public salesPerson;
- address public engineeringManager;
-
- constructor(address _salesPerson, address _engineeringManager) {
- salesPerson = _salesPerson;
- engineeringManager = _engineeringManager;
- }
-}
-```
-
----
-
-## Submit your Contracts and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-
-Submit your address for your copy of the `InheritanceSubmission` contract that contains your other contract addresses.
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/inheritance/inheritance-sbs.mdx b/docs/learn/inheritance/inheritance-sbs.mdx
deleted file mode 100644
index fb6b953e7..000000000
--- a/docs/learn/inheritance/inheritance-sbs.mdx
+++ /dev/null
@@ -1,183 +0,0 @@
----
-title: Inheritance
-description: Learn how to use inheritance to bring functionality from one contract into another.
-hide_table_of_contents: false
-sidebarTitle: Step by Step Guide
----
-
-Solidity is an object-oriented language. Contracts can inherit from one another, allowing efficient reuse of code.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Write a smart contract that inherits from another contract
-- Describe the impact inheritance has on the byte code size limit
-
----
-
-## Inheritance
-
-Create a new contract file in Remix called `Inheritance.sol` and add two simple contracts, each with a function identifying which contract called it:
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-contract ContractB {
- function whoAmI() external pure returns (string memory) {
- return "contract B";
- }
-}
-
-contract ContractA {
- function whoAmI() external pure returns (string memory) {
- return "contract A";
- }
-}
-```
-
-`ContractA` says that it is "contract A" and `ContractB` says that it is "contract B".
-
-### Inheriting from Another Contract
-
-[Inheritance] between contracts is indicated by the `is` keyword in the contract declaration. Update `ContractA` so that it `is` `ContractB`, and delete the `whoAmI` function from `ContractA`.
-
-
-
-```solidity
-contract ContractB {
- function whoAmI() external pure returns (string memory) {
- return "contract B";
- }
-}
-
-contract ContractA is ContractB {
-
-}
-```
-
-
-
-Deploy and test again. Even though `ContractA` doesn't have any functions in it, the deployment still shows the button to call `whoAmI`. Call it. `ContractA` now reports that it is "contract B", due to the inheritance of the function from `Contract B`.
-
-### Internal Functions and Inheritance
-
-Contracts can call the `internal` functions from contracts they inherit from. Add an `internal` function to `ContractB` called `whoAmIInternal` that returns "contract B".
-
-Add an external function called `whoAmIExternal` that returns the results of a call to `whoAmIInternal`.
-
-
-
-```solidity
-contract ContractB {
- function whoAmI() external pure returns (string memory) {
- return "contract B";
- }
-
- function whoAmIInternal() internal pure returns (string memory) {
- return "contract B";
- }
-}
-
-contract ContractA is ContractB {
- function whoAmExternal() external pure returns (string memory) {
- return whoAmIInternal();
- }
-}
-```
-
-
-
-Deploy and test. Note that in the deployment for `ContractB`, the `whoAmIInternal` function is **not** available, as it is `internal`. However, calling `whoAmIExternal` can call the `internal` function and return the expected result of "contract B".
-
-### Internal vs. Private
-
-You cannot call a `private` function from a contract that inherits from the contract containing that function.
-
-```solidity
-// Bad code example, do not use
-contract ContractB {
- function whoAmIPrivate() private pure returns (string memory) {
- return "contract B";
- }
-}
-
-contract ContractA is ContractB {
- function whoAmExternal() external pure returns (string memory) {
- return whoAmIPrivate();
- }
-}
-```
-
-The compiler will raise an error:
-
-```text
-from solidity:
-DeclarationError: Undeclared identifier.
- --> contracts/Inheritance.sol:17:16:
- |
-17 | return whoAmIPrivate();
- | ^^^^^^^^^^^^^
-```
-
-### Inheritance and Contract Size
-
-A contract that inherits from another contract will have that contract's bytecode included within its own. You can view this by opening settings in Remix and turning _Artifact Generation_ back on. The bytecode for each compiled contract will be present in the JSON file matching that contract's name within the `artifacts` folder.
-
-Any empty contract:
-
-```solidity
-contract EmptyContract {
-
-}
-```
-
-Will compile into something similar to this:
-
-```text
-6080604052600080fdfea2646970667358221220df894b82f904e22617d7e40150306e2d2e8cb2ca5dcacb666a0c3d40f5f988c464736f6c63430008110033
-```
-
-A slightly more complex contract:
-
-```solidity
-contract notEmptyContract {
- function sayHello() public pure returns (string memory) {
- return "To whom it may concern, I write you after a long period of silence to alert you that after much reflection, it occurs to me that I don't think you have fully considered...";
- }
-}
-```
-
-Will have more complex bytecode. In this case, mostly to store the long string present in the return:
-
-```text
-608060405234801561001057600080fd5b50610201806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063ef5fb05b14610030575b600080fd5b61003861004e565b60405161004591906100fe565b60405180910390f35b60606040518060e0016040528060ab815260200161012160ab9139905090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100a857808201518184015260208101905061008d565b60008484015250505050565b6000601f19601f8301169050919050565b60006100d08261006e565b6100da8185610079565b93506100ea81856020860161008a565b6100f3816100b4565b840191505092915050565b6000602082019050818103600083015261011881846100c5565b90509291505056fe546f2077686f6d206974206d617920636f6e6365726e2c204920777269746520796f752061667465722061206c6f6e6720706572696f64206f662073696c656e636520746f20616c65727420796f752074686174206166746572206d756368207265666c656374696f6e2c206974206f636375727320746f206d652074686174204920646f6e2774207468696e6b20796f7520686176652066756c6c7920636f6e736964657265642e2e2ea264697066735822122058d68a2853aaa473c9a5ff4dba0cc94657cb2a5a87ce3a986090a7ab991055a464736f6c63430008110033
-```
-
-However, if the empty contract inherits from the not empty contract:
-
-```solidity
-contract EmptyContract is notEmptyContract {
-
-}
-```
-
-The resulting bytecode will include that of the contract inherited from:
-
-```text
-608060405234801561001057600080fd5b50610201806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063ef5fb05b14610030575b600080fd5b61003861004e565b60405161004591906100fe565b60405180910390f35b60606040518060e0016040528060ab815260200161012160ab9139905090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100a857808201518184015260208101905061008d565b60008484015250505050565b6000601f19601f8301169050919050565b60006100d08261006e565b6100da8185610079565b93506100ea81856020860161008a565b6100f3816100b4565b840191505092915050565b6000602082019050818103600083015261011881846100c5565b90509291505056fe546f2077686f6d206974206d617920636f6e6365726e2c204920777269746520796f752061667465722061206c6f6e6720706572696f64206f662073696c656e636520746f20616c65727420796f752074686174206166746572206d756368207265666c656374696f6e2c206974206f636375727320746f206d652074686174204920646f6e2774207468696e6b20796f7520686176652066756c6c7920636f6e736964657265642e2e2ea264697066735822122088e486b0a77cd3e2ce809e0a086052815913daec73ebd731e30496d650784f7664736f6c63430008110033
-```
-
----
-
-## Conclusion
-
-In this lesson, you've learned how to use inheritance to include the functionality of one contract in another. You've also learned that inheriting contracts can call `internal` functions, but they cannot call `private` functions. You've also learned that inheriting from a contract adds the size of that contract's bytecode to the total deployed size.
-
----
-
-[Inheritance]: https://docs.soliditylang.org/en/v0.8.17/contracts.html
diff --git a/docs/learn/inheritance/inheritance-vid.mdx b/docs/learn/inheritance/inheritance-vid.mdx
deleted file mode 100644
index f0644d6bf..000000000
--- a/docs/learn/inheritance/inheritance-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Inheritance
-sidebarTitle: Inheritance Overview
-description: Create contracts that inherit from other contracts.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/inheritance/multiple-inheritance-vid.mdx b/docs/learn/inheritance/multiple-inheritance-vid.mdx
deleted file mode 100644
index 1a9be6213..000000000
--- a/docs/learn/inheritance/multiple-inheritance-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Multiple Inheritance
-description: Create contracts that inherit from multiple contracts.
-hide_table_of_contents: false
----
-
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/inheritance/multiple-inheritance.mdx b/docs/learn/inheritance/multiple-inheritance.mdx
deleted file mode 100644
index 5dfee1d20..000000000
--- a/docs/learn/inheritance/multiple-inheritance.mdx
+++ /dev/null
@@ -1,268 +0,0 @@
----
-title: Multiple Inheritance
-sidebarTitle: Multiple Inheritance Guide
-description: Learn how to have a contract inherit from multiple contracts.
-hide_table_of_contents: false
----
-
-Contracts can inherit from more than one contract. In this lesson, we'll explore how multiple inheritance works in Solidity.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Write a smart contract that inherits from multiple contracts
-
----
-
-## Multiple Inheritance
-
-Continue working with your contracts in `Inheritance.sol`. Add a new contract called `ContractC` with another `whoAmI` function:
-
-
-
-```solidity
-contract ContractC {
- function whoAmI() external pure returns (string memory) {
- return "contract C";
- }
-}
-```
-
-
-
-### Inheriting from Two Contracts
-
-You can inherit from additional contracts by simply adding a comma and that contract's name after the first. Add inheritance from `ContractC` (an error is expected):
-
-
-
-```solidity
-// bad code example, do not use
-contract ContractA is ContractB, ContractC {
- function whoAmExternal() external pure returns (string memory) {
- return whoAmIInternal();
- }
-}
-```
-
-
-
-The error is because both `ContractB` and `ContractC` contain a function called `whoAmI`. As a result, the compiler needs instruction on which to use.
-
-```text
-from solidity:
-TypeError: Derived contract must override function "whoAmI". Two or more base classes define function with same name and parameter types.
- --> contracts/Inheritance.sol:21:1:
- |
-21 | contract ContractA is ContractB, ContractC {
- | ^ (Relevant source part starts here and spans across multiple lines).
-Note: Definition in "ContractC":
- --> contracts/Inheritance.sol:6:5:
- |
-6 | function whoAmI() external pure returns (string memory) {
- | ^ (Relevant source part starts here and spans across multiple lines).
-Note: Definition in "ContractB":
- --> contracts/Inheritance.sol:12:5:
- |
-12 | function whoAmI() external pure returns (string memory) {
- | ^ (Relevant source part starts here and spans across multiple lines).
-```
-
-### Using Virtual and Override
-
-One method to resolve this conflict is to use the [`virtual` and `override`] keywords to enable you to add functionality to choose which to call.
-
-Add the `virtual` keyword to the `whoAmI` function in both `ContractC` and `ContractB`.
-
-They must also be made `public` instead of `external`, because `external` functions cannot be called within the contract.
-
-```solidity
-contract ContractC {
- function whoAmI() public virtual pure returns (string memory) {
- return "contract C";
- }
-}
-
-contract ContractB {
- function whoAmI() public virtual pure returns (string memory) {
- return "contract B";
- }
-
- // ... additional code
-}
-```
-
-Add an `override` function called `whoAmI` to `ContractA`:
-
-```solidity
-// Bad code example, do not use
-function whoAmI() public override pure returns (string memory) {
- return ContractB.whoAmI();
-}
-```
-
-You'll get another error, telling you to specify which contracts this function should override.
-
-```text
-from solidity:
-TypeError: Function needs to specify overridden contracts "ContractB" and "ContractC".
- --> contracts/Inheritance.sol:22:32:
- |
-22 | function whoAmI() public override pure returns (string memory) {
- | ^^^^^^^^
-```
-
-Add them both:
-
-```solidity
-function whoAmI() external override(ContractB, ContractC) pure returns (string memory) {
- return ContractB.whoAmI();
-}
-```
-
-Deploy and test. The call will now be back to reporting "contract B".
-
-### Changing Types Dynamically
-
-Add an `enum` at the contract level in `ContractA` with members for `None`, `ContractBType`, and `ContractCType`, and an instance of it called `contractType`.
-
-
-
-```solidity
-enum Type { None, ContractBType, ContractCType }
-
-Type contractType;
-```
-
-
-
-Add a `constructor` to `ContractA` that accepts a `Type` and sets `initialType`.
-
-
-
-```solidity
-constructor (Type _initialType) {
- contractType = _initialType;
-}
-```
-
-
-
-Update `whoAmI` in `ContractA` to call the appropriate `virtual` function based on its `currentType`.
-
-
-
-```solidity
-// Bad code example, do not use
-function whoAmI() public override(ContractB, ContractC) pure returns (string memory) {
- if(contractType == Type.ContractBType) {
- return ContractB.whoAmI();
- }
- if(contractType == Type.ContractCType) {
- return ContractC.whoAmI();
- }
- return "contract A";
-}
-```
-
-
-
-You'll get errors because the function now reads from state, so it is no longer `pure`. Update it to `view`. You'll also have to update the `whoAmI` `virtual` functions to `view` to match.
-
-
-
-```solidity
-function whoAmI() public override(ContractB, ContractC) view returns (string memory) {
- if(contractType == Type.ContractBType) {
- return ContractB.whoAmI();
- }
- if(contractType == Type.ContractCType) {
- return ContractC.whoAmI();
- }
- return "contract A";
-}
-```
-
-
-
-Finally, add a function that allows you to switch `currentType`:
-
-
-
-```solidity
-function changeType(Type _newType) external {
- contractType = _newType;
-}
-```
-
-
-
-Deploy and test. You'll need to use **0**, **1**, and **2** as values to set `contractType`, because Remix won't know about your `enum`.
-
-## Final Code
-
-After completing this exercise, you should have something similar to:
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-contract ContractC {
- function whoAmI() public virtual view returns (string memory) {
- return "contract C";
- }
-}
-
-contract ContractB {
- function whoAmI() public virtual view returns (string memory) {
- return "contract B";
- }
-
- function whoAmIInternal() internal pure returns (string memory) {
- return "contract B";
- }
-}
-
-contract ContractA is ContractB, ContractC {
- enum Type { None, ContractBType, ContractCType }
-
- Type contractType;
-
- constructor (Type _initialType) {
- contractType = _initialType;
- }
-
- function changeType(Type _newType) external {
- contractType = _newType;
- }
-
- function whoAmI() public override(ContractB, ContractC) view returns (string memory) {
- if(contractType == Type.ContractBType) {
- return ContractB.whoAmI();
- }
- if(contractType == Type.ContractCType) {
- return ContractC.whoAmI();
- }
- return "contract A";
- }
-
- function whoAmExternal() external pure returns (string memory) {
- return whoAmIInternal();
- }
-}
-```
-
----
-
-## Conclusion
-
-In this lesson, you've explored how to use multiple inheritance to import additional functionality into a contract. You've also implemented one approach to resolving name conflicts between those contracts.
-
----
-
-[`virtual` and `override`]: https://docs.soliditylang.org/en/v0.8.17/contracts.html?#function-overriding
-
diff --git a/docs/learn/interfaces/calling-another-contract-vid.mdx b/docs/learn/interfaces/calling-another-contract-vid.mdx
deleted file mode 100644
index a8c524a9f..000000000
--- a/docs/learn/interfaces/calling-another-contract-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Calling Another Contract
-description: Call the functions in another contract from your own contract.
-hide_table_of_contents: false
----
-
-This tutorial has been moved as part of a reorganization! It assumes you are using Hardhat. Everything in this lesson will work with minor adjustments if you are working in Foundry or Remix.
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/interfaces/contract-to-contract-interaction.mdx b/docs/learn/interfaces/contract-to-contract-interaction.mdx
deleted file mode 100644
index a0a994912..000000000
--- a/docs/learn/interfaces/contract-to-contract-interaction.mdx
+++ /dev/null
@@ -1,266 +0,0 @@
----
-title: 'Contract to Contract Interaction'
-sidebarTitle: Step by Step Guide
-description: Interact with other smart contracts
-hide_table_of_contents: false
----
-
-In this article, you'll learn how to interact with other smart contracts using interfaces and the `.call()` function, which allows you to interact with other smart contracts without using an interface.
-
-
-This tutorial has been moved as part of a reorganization! It assumes you are using Hardhat. Everything in this lesson will work with minor adjustments if you are working in Foundry or Remix.
-
-
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Use interfaces to allow a smart contract to call functions in another smart contract
-- Use the `call()` function to interact with another contract without using an interface
-
----
-
-## Overview
-
-Interacting with external smart contracts is a very common task in the life of a smart contract developer. This includes interacting with contracts that are already deployed to a particular network.
-
-Usually the creators of certain smart contracts document their functionality and expose their functions by providing interfaces that can be used to integrate those particular contracts into your own.
-
-For instance, [Uniswap] provides documentation on how to interact with their smart contracts and also some packages to easily integrate their protocol.
-
-In this example, you interact with the [Uniswap protocol] to create a custom pool for a custom pair of tokens.
-
-Since the Uniswap protocol is already deployed, you will use [Hardhat forking] to test your contract.
-
-You will also use the following two approaches in the example:
-
-- Using interfaces
-- Using the `.call()` function
-
-## Interacting with deployed contracts using interfaces
-
-You must first install the [Uniswap V3 core package] by running:
-
-```bash
-npm install @uniswap/v3-core
-```
-
-This package provides access to the Uniswap interfaces of the Core protocol.
-
-Then, write a custom contract called `PoolCreator` with the following code:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
-
-contract PoolCreator {
- IUniswapV3Factory public uniswapFactory;
-
- constructor(address _factoryAddress) {
- uniswapFactory = IUniswapV3Factory(_factoryAddress);
- }
-
- function createPool(
- address tokenA,
- address tokenB,
- uint24 fee
- ) external returns (address poolAddress) {
- // Check if a pool with the given tokens and fee already exists
- poolAddress = uniswapFactory.getPool(tokenA, tokenB, fee);
- if (poolAddress == address(0)) {
- // If the pool doesn't exist, create a new one
- poolAddress = uniswapFactory.createPool(tokenA, tokenB, fee);
- }
-
- return poolAddress;
- }
-}
-```
-
-Notice the following:
-
-- You are importing a `IUniswapV3Factory` interface. The interface contains function declarations that include `getPool` and `createPool`:
-
-```solidity
-// SPDX-License-Identifier: GPL-2.0-or-later
-pragma solidity >=0.5.0;
-
-/// @title The interface for the Uniswap V3 Factory
-/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
-interface IUniswapV3Factory {
- // ...
- // ...other function declarations
-
- /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
- /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
- /// @param tokenA The contract address of either token0 or token1
- /// @param tokenB The contract address of the other token
- /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
- /// @return pool The pool address
- function getPool(
- address tokenA,
- address tokenB,
- uint24 fee
- ) external view returns (address pool);
-
- /// @notice Creates a pool for the given two tokens and fee
- /// @param tokenA One of the two tokens in the desired pool
- /// @param tokenB The other of the two tokens in the desired pool
- /// @param fee The desired fee for the pool
- /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
- /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
- /// are invalid.
- /// @return pool The address of the newly created pool
- function createPool(
- address tokenA,
- address tokenB,
- uint24 fee
- ) external returns (address pool);
-```
-
-- The constructor receives the address of the pool factory and creates an instance of `IUniswapV3Factory`.
-- The `createPool` function includes a validation to ensure the pool doesn't exist.
-- The `createPool` function creates a new pool.
-
-Then, create a test file called `PoolCreator.test.ts` with the following content:
-
-```tsx
-import { ethers } from 'hardhat';
-import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';
-
-import { Token, Token__factory, PoolCreator, PoolCreator__factory } from '../typechain-types';
-
-describe('PoolCreator tests', function () {
- const UNISWAP_FACTORY_ADDRESS = '0x1F98431c8aD98523631AE4a59f267346ea31F984';
- let tokenA: Token;
- let tokenB: Token;
- let poolCreator: PoolCreator;
- let owner: HardhatEthersSigner;
-
- before(async () => {
- const signers = await ethers.getSigners();
- owner = signers[0];
- tokenA = await new Token__factory().connect(owner).deploy('TokenA', 'TokenA');
- tokenB = await new Token__factory().connect(owner).deploy('TokenB', 'TokenB');
- poolCreator = await new PoolCreator__factory().connect(owner).deploy(UNISWAP_FACTORY_ADDRESS);
- });
-
- it('should create a pool', async () => {
- const contractAddress = await poolCreator.createPool.staticCall(tokenA, tokenB, 500);
- console.log('Contract Address', contractAddress);
- await poolCreator.createPool(tokenA, tokenB, 500);
- });
-});
-```
-
-Notice the following:
-
-- The address `0x1F98431c8aD98523631AE4a59f267346ea31F984` is the address of the Uniswap pool factory deployed to the Ethereum mainnet. This can be verified by looking at the Uniswap documentation that includes the [Deployment addresses of the contracts].
-- You created two tokens, TokenA and TokenB, by using a `Token` contract.
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
-
-contract Token is ERC20 {
- constructor(string memory name, string memory symbol) ERC20(name, symbol){
- _mint(msg.sender, 1000 ether);
- }
-}
-```
-
-Finally, run `npx hardhat test` and you should get a result similar to the following:
-
-```
-PoolCreator tests
-Contract Address 0xa76662f79A5bC06e459d0a841190C7a4e093b04d
- ✔ should create a pool (1284ms)
-
- 1 passing (5s)
-```
-
-## Interacting with external contracts using `.call()`
-
-In the previous example, you accessed the Uniswap V3 Factory interface, however if you don't have access to the contract interface, you can use a special function called `call`.
-
-Using `call`, you can call any contract as long as you know minimal information of the function signature. In this case, you should at least know that `createPool` requires three parameters:
-
-- tokenA
-- tokenB
-- fee
-
-The newly modified smart contract code looks as follows:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.19;
-
-contract PoolCreator {
- address public uniswapFactory;
-
- constructor(address _factoryAddress) {
- uniswapFactory = _factoryAddress;
- }
-
- function createPool(
- address tokenA,
- address tokenB,
- uint24 fee
- ) external returns (address poolAddress) {
- bytes memory payload = abi.encodeWithSignature(
- "createPool(address,address,uint24)",
- tokenA,
- tokenB,
- fee
- );
-
- (bool success, bytes memory data) = uniswapFactory.call(payload);
- require(success, "Uniswap factory call failed");
-
- // The pool address should be returned as the first 32 bytes of the data
- assembly {
- poolAddress := mload(add(data, 32))
- }
-
- require(poolAddress != address(0), "Pool creation failed");
- return poolAddress;
- }
-}
-```
-
-Notice the following:
-
-- By using `abi.encodeWithSignature`, you encode the payload required to make a smart contract call using the `.call()` function.
-- Using `.call()` doesn't require you to import the interface.
-- You load the pool address by using a special assembly operation called `mload`.
-
-Try to run again the command `npx hardhat test` and you should expect the same result:
-
-```
-PoolCreator tests
-Contract Address 0xa76662f79A5bC06e459d0a841190C7a4e093b04d
- ✔ should create a pool (1284ms)
-
- 1 passing (5s)
-```
-
-## Conclusion
-
-Interfaces or the `.call` function are two ways to interact with external contracts. Using interfaces provides several advantages, including type safety, code readability, and compiler error checking. When interacting with well-documented contracts like Uniswap, using interfaces is often the preferred and safest approach.
-
-On the other hand, the `.call` function offers more flexibility but comes with greater responsibility. It allows developers to call functions on contracts even without prior knowledge of their interfaces. However, it lacks the type safety and error checking provided by interfaces, making it more error-prone.
-
----
-
-[Uniswap]: https://docs.uniswap.org/contracts/v3/reference/core/UniswapV3Factory
-[Uniswap protocol]: https://uniswap.org
-[Hardhat forking]: https://hardhat.org/hardhat-network/docs/guides/forking-other-networks
-[Uniswap V3 core package]: https://www.npmjs.com/package/@uniswap/v3-core
-[Deployment addresses of the contracts]: https://docs.uniswap.org/contracts/v3/reference/deployments
diff --git a/docs/learn/interfaces/intro-to-interfaces-vid.mdx b/docs/learn/interfaces/intro-to-interfaces-vid.mdx
deleted file mode 100644
index c53bbc64e..000000000
--- a/docs/learn/interfaces/intro-to-interfaces-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Intro to Interfaces
-description: Use interfaces to tell your contract how another works.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-This tutorial has been moved as part of a reorganization! It assumes you are using Hardhat. Everything in this lesson will work with minor adjustments if you are working in Foundry or Remix.
-
-
diff --git a/docs/learn/interfaces/testing-the-interface-vid.mdx b/docs/learn/interfaces/testing-the-interface-vid.mdx
deleted file mode 100644
index 542579790..000000000
--- a/docs/learn/interfaces/testing-the-interface-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Testing the Interface
-description: Start writing tests for interfaces.
-hide_table_of_contents: false
----
-
-This tutorial has been moved as part of a reorganization! It assumes you are using Hardhat. Everything in this lesson will work with minor adjustments if you are working in Foundry or Remix.
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/introduction-to-ethereum/ethereum-applications.mdx b/docs/learn/introduction-to-ethereum/ethereum-applications.mdx
deleted file mode 100644
index 8e76c3363..000000000
--- a/docs/learn/introduction-to-ethereum/ethereum-applications.mdx
+++ /dev/null
@@ -1,193 +0,0 @@
----
-title: Ethereum Applications
-description: An overview of the development ethos of Ethereum, applications built on its network, and a high-level comparison of Web2 and Web3
----
-
-In this article, we'll explore Ethereum's significance and impact in the crypto ecosystem as well as its role in shaping the Web3 landscape. We'll learn about Ethereum's ethos and goals and also examine the different types of applications developed on Ethereum. Lastly, we'll take a look at the evolution of the web with an emphasis on comparing Web2 and Web3 development.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Describe the origin and goals of the Ethereum blockchain
-- List common types of applications that can be developed with the Ethereum blockchain
-- Compare and contrast Web2 vs. Web3 development
-- Compare and contrast the concept of "ownership" in Web2 vs. Web3
-
----
-
-## The Ethos and Goals of Ethereum
-
-Ethereum was originally proposed in 2013 by Vitalik Buterin, who was then a young developer in the Bitcoin community. Vitalik had a vision that the potential of blockchain technology extended far beyond a decentralized digital currency. When his ideas were rejected by the Bitcoin community, he set out to create a platform that could bring his vision to life.
-
-The ethos of Ethereum is fundamentally different from Bitcoin's. Bitcoin development is conservative; it's focused on maintaining the existing protocol, making only incremental improvements over time rather than implementing radical changes. In other words, changes are slow and deliberate and any unnecessary risk-taking is generally frowned upon. Ethereum development, on the other hand, is focused on innovation and experimentation. There is more of a willingness to take risks and make radical changes to the protocol in order to improve on and expand upon functionality and enable new use cases.
-
-Ethereum's primary goal is to be a general, all-purpose blockchain that allows developers to create any type of decentralized application that their minds can conjure up. One of the most important features that unlock all of these possibilities is smart contracts. Without smart contract functionality, most applications built on the platform today would be nonexistent.
-
-
-
-
-
----
-
-## Applications on Ethereum
-
-Before delving into the different types of applications built on Ethereum, let's review one of the underlying forces of the smart contracts that make them possible.
-
-### Scripting
-
-As we've learned, one of Bitcoin's limitations when it comes to complex applications being built on the protocol is scripting. Its simple stack-based, left-to-right scripting system lacks the flexibility to support smart contracts. In the Ethereum whitepaper, Vitalik pointed out several limitations to Bitcoin scripting. A couple of key ones to note are:
-
-- Lack of Turing-completeness. Although this is an intentional feature of Bitcoin to avoid infinite loops during transaction verification, without loops or recursion, running a complex program that a decentralized application demands, is not possible.
-
-- Lack of state. Bitcoin's UTXO model only allows for simple contracts and not the complex stateful contracts needed for most decentralized applications. What that means is that there is no internal state beyond a UTXO being spent or unspent.
-
-Ethereum's scripting languages, most notably Solidity, are Turing-complete and stateful, among other features. These features allow smart contracts to be executed deterministically, meaning that the outcome of the contract is predictable and can be enforced automatically and autonomously. They also allow developers to write much more complex programs that can execute a much wider range of operations.
-
-It is this flexibility and versatility in Ethereum's scripting that ultimately powers the decentralized applications that we know.
-
-### Decentralized Finance (DeFi)
-
-DeFi is one of the most popular use cases for Ethereum. In DeFi Summer 2020, we saw an explosion in the usage and popularity of DeFi applications built on Ethereum. During this brief period alone, the total value locked in DeFi protocols increased from less than $1 billion to more than $10 billion. This period marked a turning point, as it brought more attention to the space, and it solidified Ethereum as the de facto smart contract platform of the DeFi and greater Web3 ecosystem.
-
-DeFi applications are designed to provide traditional financial services, such as lending, borrowing, trading, and much more, in a transparent, open and accessible manner. All of these services are facilitated by smart contracts.
-
-How exactly does this work? Let's take a look at a simple example in the context of a DeFi lending platform, such as Aave or Compound.
-
-Suppose Alice wants to borrow 5 ETH but doesn't want to sell her 10,000 USDC. She can deposit that USDC as collateral and borrow 5 ETH against it.
-
-First, Alice must interact with the smart contract of the platform. The smart contract will check if Alice has 10,000 USDC in her wallet and, if so, will lock it up as collateral for the loan. The smart contract then transfers 5 ETH to Alice's wallet. The smart contract will also define the terms of the loan, such as the interest rate and the repayment date.
-
-Alice now has 5 ETH that she can use for whatever she wants. However, she must repay the loan within the specified period. If Alice repays the loan on time, the smart contract will release her deposited collateral back to her. Otherwise, it will automatically liquidate her collateral and transfer it to the lender's address in exchange for the 5 ETH that was lent to her. The repayment will also include any accrued interest based on the interest rate set by the smart contract.
-
-In this way, smart contracts enable DeFi platforms to operate autonomously without a centralized entity. The smart contract provides security and transparency to both the borrower and the lender, as the terms of the loan are defined in the code and enforced automatically.
-
-Of course, this does not mean that DeFi comes without risks. Although it's beyond the scope of this article, it's worth mentioning that DeFi has been the target of numerous smart contract exploits involving hundreds of millions of dollars in value.
-
-### Non-Fungible Token (NFT)
-
-NFTs are another application of Ethereum that has gained significant attention in recent years. NFTs are unique digital assets that represent ownership of a specific item. They can be used to represent just about anything, but most notably digital artwork, sports collectibles, and in-game items.
-
-Smart contracts play a crucial role in NFTs by providing a way to represent and enforce ownership of the digital asset. When a new NFT is created, a smart contract is deployed on Ethereum that defines the unique characteristics of that asset as well as the ownership information and rules for transferring ownership.
-
-Ethereum's ERC-721 standard was the first to introduce NFTs in 2017, and it has since become the most popular standard for creating and trading NFTs on Ethereum.
-
-We'll cover more on tokens and token standards for fungible and non-fungible assets later on in the course.
-
-### Decentralized Autonomous Organization (DAO)
-
-DAOs are another common use case for Ethereum and also one of the earliest use cases implemented on the network. In simple words, DAOs are software-enabled, community-led organizations. They allow a community to pool resources toward a shared goal, such as [buying one of the original copies of the U.S. Constitution](https://www.theverge.com/22820563/constitution-meme-47-million-crypto-crowdfunding-blockchain-ethereum-constitution) or determining the future of a protocol. Because DAOs aren't tied to a physical location, they are able to mobilize quickly and
-attract resources and capital from all over the world.
-
-The rules of a DAO are established by community members through the use of smart contracts, which lay the groundwork for how a DAO operates. When a new DAO is created, a contract is deployed to the network. It contains the rules that govern the organization, including how its resources are managed. Members of the DAO can then interact with the contract by sending transactions to the blockchain.
-
-DAOs typically use a token-based system to govern voting and decision-making. Members of the DAO are issued tokens that represent their ownership and influence within the organization. These tokens can be used to vote on proposals and allocate resources.
-
-When a proposal is submitted to the DAO, members can vote on whether to accept or reject it. The smart contract tracks the votes and automatically executes the proposal if it receives enough support from the members. This process allows members of the DAO to collectively make decisions and take actions in a decentralized way.
-
-### Other Applications
-
-While the above use cases have been the most prominent applications on Ethereum, there are a plethora of others, including:
-
-- **Identity Management** is one use case that has come to the forefront in recent years. The most notable example is the Ethereum Name Service (ENS). It allows users to register human-readable domain names, similar to the traditional DNS system, but with the added functionality of being able to associate Ethereum and other blockchain addresses with a domain name, such as `vitalik.eth`. This makes it easier for users to send and receive transactions without having to remember or type in long and complex addresses.
-
-- **Gaming** is another common use case. Axie Infinity and Decentraland are both popular examples of decentralized games that make use of fungible and non-fungible tokens for a variety of purposes.
-
-- **Prediction markets** are another use case where users can bet on the outcome of real-world events, such as forecasting election results or predicting which team will win a game. Augur and Gnosis are popular examples of this application.
-
-There are many other use cases from supply chain, energy, and intellectual property management to decentralized storage and content management to governance and voting systems. The list goes on and on, and it's worth taking some time to explore these types of applications on your own.
-
----
-
-## Evolution of the Web
-
-Opinions abound in the history, divisions, and eras of the development and evolution of the internet. One popular explanation divides the web into three major eras (so far).
-
-#### Web1
-
-The web has come a long way since its humble read-only beginnings in the early 1990s. Web1 is often referred to as the _static web_, meaning it was primarily a collection of static web pages that provided information to users with limited interaction and dynamic content.
-
-#### Web2
-
-In the early 2000s, a _dynamic web_ emerged. Web2 introduced more interactive and dynamic content, such as social media and e-commerce. It's characterized by the use of centralized servers that store and control user data. In other words, with Web2, users can interact with content, share information, and collaborate with others, but they have limited control over their data. A vast majority of the web today operates in this paradigm.
-
-#### Web3
-
-Web3 or the _decentralized web_ is the next phase of the web that has started to emerge in recent years with the rise in popularity of Ethereum. This iteration of the web is focused on user ownership and control over data, providing a more private, decentralized, and secure web experience.
-
-### The Limitations of Web2
-
-While Web2 brought many benefits beyond its predecessor, there are some key limitations to consider.
-
-- **Privacy & Control:** Users have limited control over their data and how it is used. Companies often have broad or even complete control over user data on a given platform.
-
-- **Censorship:** Due to centralized control of user data, corporations or governments can censor content they deem as inappropriate or dangerous, which can limit free speech and expression or block access to certain online services. This is especially concerning in countries with authoritarian regimes, which often use censorship as a tool to control citizens and maintain power.
-
-- **Lack of transparency:** Users cannot always verify how their data is being used or who has access to it.
-
-- **Security vulnerabilities:** Because Web2 relies on centralized servers, it is more vulnerable to hacking and data breaches, which can expose sensitive user information and compromise online safety.
-
-- **Limited interoperability:** Most Web2 platforms are not interoperable, meaning that different platforms may not be compatible with each other. Data is generally confined to one system.
-
-### The Limitations of Web3
-
-While many of the limitations of Web2 have spurred the development of Web3 by aiming to provide a more decentralized, secure, and private web, Web3 does not come without its own set of limitations.
-
-- **Speed:** The reliance on decentralized networks and consensus mechanisms result in much slower processing times compared to centralized systems.
-
-- **Storage:** Storing data onchain can be very expensive, which can make it challenging for developers to create apps that require large amounts of storage.
-
-- **Smart contract limitations:**
-
- - Smart contracts on Ethereum are currently limited to a maximum size of 24 KB, which can limit the complexity of the logic that can be programmed into them.
-
- - Once a smart contract is deployed, it cannot be updated or changed. If there is a bug or a flaw in the contract, it cannot be fixed unless a new contract is deployed.
-
-- **All data is public:** The transparency of blockchain means that all data is public and visible to anyone. While this can be an advantage in terms of transparency and accountability, it can also be a limitation for applications that may require privacy or confidentiality.
-
-
-
-
-
-### Web2 vs Web3 Development
-
-While there are many general distinctions to be made between Web2 and Web3, these characteristics are even more apparent when examining their development approaches.
-
-#### Web2
-
-In Web2, engineering is centered around a client-server architecture, and development is focused on building applications for specific platforms and using APIs and tools provided by those platforms to create user interfaces and access data. There is a top-down corporate approach to development processes, and code is generally proprietary and closed-sourced. As a result, there tends to be very limited collaboration with developers outside of a company and there is little integration between different platforms.
-
-Web2 developers rely on centralized infrastructure, such as servers and cloud-computing services provided by large tech companies to host their applications and store data. This creates a centralized system where the platforms and companies that control the infrastructure have significant power and control over the applications and data that are built on top of them.
-
-#### Web3
-
-In contrast, the Web3 development paradigm is centered around a distributed architecture, where developers build applications that run on decentralized protocols and smart contracts. There is a bottom-up community approach to development processes, and there is an emphasis on open-source code and open standards. Web3 development culture is collaborative, and there is strong integration and interoperability between platforms.
-
-Web3 development requires a different set of engineering skills and tools. Developers need to have a strong understanding of blockchain technology, cryptography, and distributed systems, and they also need to be proficient in programming languages like Solidity.
-
-There is also a different approach to testing and deployment. Because onchain apps run on distributed systems, developers need to consider factors like network latency and the possibility of network partitions. They also need to ensure that their applications are extremely secure and resistant to a variety of attacks because the stakes can often be very high when it comes to dealing with millions and even billions of dollars of value that cannot be recovered in the event of a hack. Developers also have to consider concepts like immutability because once code is deployed to a blockchain, it cannot be edited.
-
-Overall, Web3 development requires a different set of engineering skills and tools as well as a deeper understanding of distributed systems and cryptography. Developers also need the ability to think creatively about how to build applications that are constrained by the technical limitations of the Web3 paradigm, such as speed, storage, and scalability.
-
-
-
-
-
----
-
-## Conclusion
-
-Ethereum was created to extend the potential of blockchain technology beyond just a decentralized digital currency platform. Its ethos of innovation and experimentation has made a major impact on shaping the crypto ecosystem and has played a significant role in shaping the landscape of Web3. The use of smart contracts has enabled a wide range of new web applications, including DeFi, NFTs, DAOs, and many more.
-
-The evolution of the web has brought us from a static Web1 to a dynamic Web2, and now to a decentralized Web3. While Web2 brought many key benefits, it also came with many drawbacks regarding privacy, censorship, and security vulnerabilities. Web3 aims to address these challenges but has its own set of limitations. Lastly, the development paradigms of Web2 and Web3 are distinct in their architecture, infrastructure, and their development approaches. Web3 development requires a different set of skills and a different mental framework from its predecessor.
-
----
-
-## See also
-
-- [Ethereum Whitepaper](https://ethereum.org/en/whitepaper/)
-- [Decentralized Applications (Dapps)](https://ethereum.org/en/dapps/)
-- [Web2 vs Web3](https://ethereum.org/en/developers/docs/web2-vs-web3/)
-- [The Architecture of a Web 3.0 Application](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application)
diff --git a/docs/learn/introduction-to-ethereum/ethereum-dev-overview-vid.mdx b/docs/learn/introduction-to-ethereum/ethereum-dev-overview-vid.mdx
deleted file mode 100644
index e06928563..000000000
--- a/docs/learn/introduction-to-ethereum/ethereum-dev-overview-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Ethereum Dev Overview
-description: An overview of web 3 application development.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/introduction-to-ethereum/evm-diagram.mdx b/docs/learn/introduction-to-ethereum/evm-diagram.mdx
deleted file mode 100644
index 303a63981..000000000
--- a/docs/learn/introduction-to-ethereum/evm-diagram.mdx
+++ /dev/null
@@ -1,109 +0,0 @@
----
-title: EVM Diagram
-description: An overview of the Ethereum Virtual Machine
----
-
-In this article, we'll examine the inner workings of the EVM, its components, and its role within the Ethereum network.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Diagram the EVM
-
----
-
-## What is the EVM?
-
-The Ethereum Virtual Machine (EVM) is the core engine of Ethereum. It is a Turing-complete, sandboxed virtual machine designed to execute smart contracts on the network. The term "sandboxed" means that the EVM operates in an isolated environment, ensuring that each smart contract's execution does not interfere with others or the underlying blockchain. As we've learned, the EVM's Turing-complete nature allows developers to write complex programs that can perform any computationally feasible task.
-
-The EVM employs a sophisticated resource management system using gas to regulate computation costs and prevent network abuse. It also supports a rich ecosystem of apps by providing a versatile set of opcodes for smart contract logic, and fostering interoperability with various programming languages, tools, and technologies. This adaptability has made the EVM a fundamental component in the advancement and growth of the Ethereum network.
-
----
-
-## EVM Components
-
-The EVM has several key components that enable it to process and manage smart contracts. Let's define them:
-
-- **World State:** Represents the entire Ethereum network, including all accounts and their associated storage.
-- **Accounts:** Entities that interact with the Ethereum network, including Externally Owned Accounts (EOAs) and Contract Accounts.
-- **Storage:** A key-value store associated with each contract account, containing the contract's state and data.
-- **Gas:** A mechanism for measuring the cost of executing operations in the EVM, which protects the network from spam and abuse.
-- **Opcodes:** Low-level instructions that the EVM executes during smart contract processing.
-- **Execution Stack:** A last-in, first-out (LIFO) data structure for temporarily storing values during opcode execution.
-- **Memory:** A runtime memory used by smart contracts during execution.
-- **Program Counter:** A register that keeps track of the position of the next opcode to be executed.
-- **Logs:** Events emitted by smart contracts during execution, which can be used by external systems for monitoring or reacting to specific events.
-
----
-
-## EVM Execution Model
-
-In simple terms, when a transaction is submitted to the network, the EVM first verifies its validity. If the transaction is deemed valid, the EVM establishes an execution context that incorporates the current state of the network and processes the smart contract's bytecode using opcodes. As the EVM runs the smart contract, it modifies the blockchain's world state and consumes gas accordingly. However, if the transaction is found to be invalid, it will be dismissed by the network without further processing. Throughout the smart contract's execution, logs are generated that provide insights into the contract's performance and any emitted events. These logs can be utilized by external systems for monitoring purposes or to respond to specific events.
-
-
-
-
-
----
-
-## Gas and Opcode Execution
-
-While we have already delved into the concept of gas in a previous lesson, it is worth reiterating its critical role within the EVM and as a fundamental component of Ethereum. Gas functions as a metric for quantifying the computational effort needed to carry out operations in the EVM. Every opcode in a smart contract carries a specific gas cost, which reflects the computational resources necessary for its execution.
-
-Opcodes are the low-level instructions executed by the EVM. They represent elementary operations that allow the EVM to process and manage smart contracts.
-
-
-
-
-
-During execution, the EVM reads opcodes from the smart contract, and depending on the opcode, it may update the world state, consume gas, or revert the state if an error occurs. Some common opcodes include:
-
-- **ADD:** Adds two values from the stack.
-- **SUB:** Subtracts two values from the stack.
-- **MSTORE:** Stores a value in memory.
-- **SSTORE:** Stores a value in contract storage.
-- **CALL:** Calls another contract or sends ether.
-
----
-
-## Stack and Memory
-
-The EVM stack and memory are critical components of the EVM architecture, as they enable smart contracts to manage temporary data during opcode execution. The stack is a last-in, first-out (LIFO) data structure that is used for temporarily storing values during opcode execution. It is managed by the EVM and is separate from the contract's storage. The stack supports two primary operations: push and pop.
-
-The push operation adds a value to the top of the stack, while the pop operation removes the top value from the stack. These operations are used to manage temporary data during opcode execution. For example, an opcode that performs an addition operation might push the two operands onto the stack, perform the addition, and then pop the result off the top of the stack.
-
-During contract execution, memory serves as a collection of bytes, organized in an array, for the purpose of temporarily storing data. It can be read from and written to by opcodes. Memory is often used to store temporary data during opcode execution, such as when working with dynamically sized data like strings or arrays that are being manipulated or computed within the smart contract before being stored in the contract's storage. When a smart contract needs to store temporary data during opcode execution, it can use the memory to store that data.
-
-
-
-
-
----
-
-## EVM Architecture and Execution Context
-
-To fully grasp the EVM architecture and its components, it's important to see how they all come together in a cohesive manner. The following diagram provides an in-depth visualization of the EVM architecture, showcasing the interactions between key elements such as transactions, gas, opcodes, and the world state. With this diagram, you can see how each component plays a vital role in the seamless execution of smart contracts on the Ethereum network.
-
-
-
-
-Image Source: [Mastering Ethereum](https://github.com/ethereumbook/ethereumbook) by Andreas M. Antonopoulos and Gavin Wood, licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
-
----
-
-## Conclusion
-
-The EVM plays a vital role within the Ethereum network. By examining the EVM's key components as well as its architecture and execution model, we've gained insight into the engine of Ethereum and how it enables the smooth execution of smart contracts on the platform.
-
----
-
-## See Also
-
-- [The Ethereum Virtual Machine (Mastering Ethereum)](https://cypherpunks-core.github.io/ethereumbook/13evm.html#evm_architecture)
-- [Ethereum Virtual Machine (Ethereum docs)](https://ethereum.org/en/developers/docs/evm/)
-
-
-[the ethereum virtual machine (mastering ethereum)]: https://cypherpunks-core.github.io/ethereumbook/13evm.html#evm_architecture
diff --git a/docs/learn/introduction-to-ethereum/gas-use-in-eth-transactions.mdx b/docs/learn/introduction-to-ethereum/gas-use-in-eth-transactions.mdx
deleted file mode 100644
index 8230daf72..000000000
--- a/docs/learn/introduction-to-ethereum/gas-use-in-eth-transactions.mdx
+++ /dev/null
@@ -1,117 +0,0 @@
----
-title: Gas Use in Ethereum Transactions
-description: An overview of how gas works in Ethereum
----
-
-In this article, we'll delve into the concept of gas and its importance in the Ethereum ecosystem. You'll learn why Ethereum relies on a system of gas to regulate the execution of transactions and smart contracts, and how it plays a crucial role in the proper functioning of the network.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Explain what gas is in Ethereum
-- Explain why gas is necessary in Ethereum
-- Understand how gas works in Ethereum transactions
-
----
-
-## What is gas?
-
-Gas is a term used in Ethereum to describe a computational unit that measures the amount of computational work needed to perform specific operations on the network. Unlike Bitcoin, where transaction fees only consider the size of a transaction, Ethereum accounts for every computational step performed by transactions and smart contract code execution. In other words, every single operation that is performed on Ethereum requires a certain amount of gas.
-
-### Complexity
-
-The amount of gas required for an operation depends on its complexity. More complex operations require more computational resources and therefore require more gas to be executed. For example, a simple transaction that involves sending ETH from one address to another may require less gas than a complex smart contract that executes multiple operations or interacts with multiple other contracts.
-
-### State of the Network
-
-Gas costs can also vary depending on the state of the network, or more specifically, how congested it is. When there are more transactions waiting to be processed than the network can handle, it will prioritize transactions based on the gas price that was set by the user, meaning that higher gas prices are more likely to get processed first. When the network is congested, gas prices increase to encourage more efficient use of the network's resources and decrease when network usage is lower. This dynamic pricing mechanism ensures that the Ethereum network remains accessible and functional for all users, while also incentivizing responsible and efficient use of the network's resources.
-
-
-
-
-
----
-
-## Why is gas necessary?
-
-### Turing Completeness
-
-As we've learned, Ethereum is a Turing-complete platform, which means that any program that can be represented in code can theoretically be expressed and executed on the network. This opens up the door to countless different types of applications that can be built, but it also creates the possibility that malicious or inefficient code can clog up the network, potentially leading to denial-of-service attacks, network spam, and other problems.
-
-### Preventing Infinite Loops
-
-Gas to the rescue! To prevent accidental or intentional infinite loops in smart contract code, Ethereum requires that every transaction specify a gas limit. The gas limit establishes the maximum amount of gas that the transaction can consume, and they ensure that transactions are executed within a predetermined amount of computational resources, preventing the execution of code that might consume too much computation power and potentially cause the network to freeze or crash. Without gas, Ethereum's Turing completeness would be insecure and inefficient.
-
-### Autonomous Execution
-
-It's also important to note that gas enables the execution of smart contracts without the need for a central authority to monitor their execution. The gas system provides a mechanism for regulating the resources required to execute the code of these contracts as well. In other words, without gas, it would be difficult to guarantee that smart contracts could operate autonomously, fairly and efficiently.
-
----
-
-## How does gas work?
-
-### Ethereum Denominations
-
-Before diving into the inner workings of gas, it's important to understand a few of the most common denominations used in Ethereum.
-
-#### Ether (ETH)
-
-Ether is the native cryptocurrency of the Ethereum network. Gas fees are paid in ETH.
-
-#### Wei
-
-Wei is the smallest denomination of Ethereum and is equivalent to 10^-18 ETH. It is used to represent very small amounts of ETH, usually gas prices and transaction fees. To put 10^-18 into perspective:
-
-- 1 ETH = 1,000,000,000,000,000,000 wei
-- 1 wei = 0.000000000000000001 ETH
-
-#### Gwei
-
-Gwei is commonly used to express the price of gas. One gwei is equivalent to one billionth of one ETH or 10^-9 ETH.
-
-- 1 ETH = 1,000,000,000 gwei
-- 1 gwei = 0.000000001 ETH
-
-### Gas Price
-
-Gas price on the network is denominated in gwei, and the gas fee is calculated as the product of the gas price and the amount of gas required for an operation. For example, if the gas price is 50 gwei, and an operation requires 100,000 units of gas, the gas fee would be 0.005 ETH (50 gwei x 100,000 gas = 0.005 ETH).
-
-### Gas Limit
-
-Gas limit is an essential component of the gas system in Ethereum. It defines the maximum amount of gas a user is willing to spend for a transaction to be processed. This gas limit is set by the sender of the transaction and represents the upper limit of computational resources that the transaction can consume. The Ethereum Virtual Machine (EVM) starts deducting the amount of gas used from the gas limit as soon as it starts processing the transaction.
-
-Consider Alice wants to send some ETH to Bob. Alice specifies a gas limit of 100,000 units and a gas price of 10 gwei (0.00000001 ETH) per unit of gas. So, she's willing to spend a maximum of 0.001 ETH for this transaction (1,000,000 gwei).
-
-The EVM, upon receiving Alice's transaction, starts executing it. As the transaction is processed, the EVM deducts the used gas from the gas limit. If the transaction completes before reaching the gas limit, the remaining unused gas is refunded to Alice's account.
-
-Let's illustrate this with a couple scenarios:
-
-- Suppose the transaction used 80,000 units of gas, leaving 20,000 units unused. Since the gas price was set at 10 gwei per unit, Alice would receive a refund of 0.0002 ETH (200,000 gwei) for the unused gas.
-
-- In a different scenario, suppose Alice sends a transaction with a gas limit of 100,000 units. After processing all the opcodes in the transaction except for the last one, Alice's transaction has consumed 99,998 units of gas. The EVM checks and sees that the last opcode will initiate because there are 2 units of gas remaining, enough to start it. However, as the opcode executes, it becomes clear that it actually requires more than 2 units of gas. At this point, the EVM throws an "Out of Gas" exception and halts the transaction. In this scenario, Alice loses all 100,000 units of gas, as they are consumed in the attempted execution. All state changes that might have occurred during the execution are rolled back, and the ETH Alice tried to send to Bob is returned to her.
-
-### Gas Estimation
-
-Gas estimation is another key concept to understand. It refers to the process of predicting the amount of gas that will be required to execute a transaction. This is important because as we've seen in our example, the gas limit of a transaction needs to be set before it can be broadcasted to the network. If the gas limit is set too low, the transaction may fail to execute, while if it is set too high, the sender may end up paying more in transaction fees than is necessary.
-
-There are several methods that can be used for gas estimation. One common method is to use historical gas prices and gas limits as a reference point, and to estimate the gas needed for a new transaction based on the gas used in similar past transactions. Another method is to simulate the execution of the transaction in a test environment to determine the actual amount of gas that would be used.
-
-Thankfully, most Ethereum wallet applications have built-in gas estimation algorithms that can automatically calculate an appropriate gas limit for a transaction based on the network conditions at the time the transaction is initiated. This helps to prevent a transaction from failing from the gas limit being too low while optimizing for the best possible cost for the sender.
-
----
-
-## Conclusion
-
-Gas is a vital component of Ethereum. It's what regulates the execution of all transactions and smart contracts, and it plays a significant role in the proper functioning and security of the network. Without gas, Ethereum's Turing-complete architecture would be inefficient and vulnerable to attacks. Gas also ensures that smart contracts can operate autonomously, fairly, and efficiently without the need for a central authority to monitor their execution. Understanding how gas works is essential for anyone who wants to develop applications or smart contracts on the Ethereum network.
-
----
-
-## See also
-
-- [Gas and Fees (Ethereum Docs)](https://ethereum.org/en/developers/docs/gas/)
-- [Transaction Gas (Mastering Ethereum)](https://github.com/ethereumbook/ethereumbook/blob/develop/06transactions.asciidoc#transaction-gas)
-- [Turing Completeness and Gas (Mastering Ethereum)](https://github.com/ethereumbook/ethereumbook/blob/develop/13evm.asciidoc#turing-completeness-and-gas)
-- [Gas (Mastering Ethereum)](https://github.com/ethereumbook/ethereumbook/blob/develop/13evm.asciidoc#gas)
diff --git a/docs/learn/introduction-to-ethereum/guide-to-base.mdx b/docs/learn/introduction-to-ethereum/guide-to-base.mdx
deleted file mode 100644
index befc66cde..000000000
--- a/docs/learn/introduction-to-ethereum/guide-to-base.mdx
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'Guide to Base'
-url: https://www.coinbase.com/developer-platform/discover/protocol-guides/guide-to-base
----
diff --git a/docs/learn/introduction-to-ethereum/introduction-to-ethereum-vid.mdx b/docs/learn/introduction-to-ethereum/introduction-to-ethereum-vid.mdx
deleted file mode 100644
index c1557c998..000000000
--- a/docs/learn/introduction-to-ethereum/introduction-to-ethereum-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Introduction
-description: Welcome to the world of blockchain development!
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/introduction-to-solidity/anatomy-of-a-smart-contract-vid.mdx b/docs/learn/introduction-to-solidity/anatomy-of-a-smart-contract-vid.mdx
deleted file mode 100644
index cad610667..000000000
--- a/docs/learn/introduction-to-solidity/anatomy-of-a-smart-contract-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Anatomy of a Smart Contract
-description: Review how smart contracts are organized.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/introduction-to-solidity/deployment-in-remix-vid.mdx b/docs/learn/introduction-to-solidity/deployment-in-remix-vid.mdx
deleted file mode 100644
index 40e1bb49d..000000000
--- a/docs/learn/introduction-to-solidity/deployment-in-remix-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Deployment in Remix
-description: Learn to deploy your contracts to the Remix VM.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/introduction-to-solidity/deployment-in-remix.mdx b/docs/learn/introduction-to-solidity/deployment-in-remix.mdx
deleted file mode 100644
index 922b256c3..000000000
--- a/docs/learn/introduction-to-solidity/deployment-in-remix.mdx
+++ /dev/null
@@ -1,110 +0,0 @@
----
-sidebarTitle: Step by Step Guide
-title: Deployment in Remix
-description: Use Remix to deploy and interact with a contract.
-hide_table_of_contents: false
----
-
-Remix contains a simulation of the blockchain that allows you to easily and safely deploy and interact with contracts, for free.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Deploy and test the Storage.sol demo contract in Remix
-
----
-
-## Deploy the `Storage` Contract
-
-Deploying a contract is easy, but remember that if the contract doesn't compile, the deploy button will instead deploy the last good compiled version of your contract. Verify that you see a green checkmark on the icon for the _Compiler_ contract on the left side of the editor, then select the _Deploy & Run Transactions_ plugin.
-
-If you've already deployed any contracts, press the trash can button to the right of the _Deployed Contracts_ label. Then, press the orange button to deploy the `Storage` contract.
-
-
-
-
-
-### Contract Addresses
-
-After your contract deploys, it will appear in the _Deployed_ contracts section as _STORAGE AT_ followed by an address. Addresses are used for both contracts and wallets in EVM-compatible blockchains and serve a similar purpose to an IP address. You can copy the address to see what it looks like. It's 20 characters of hexadecimal, similar to `0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8`.
-
-The address is what you will use to find your contract with tools such as _Etherscan_, or to connect to it with a front end.
-
-However, when you deploy using the Remix VM simulation, it will only exist in your browser.
-
-### Deployments and Test Accounts
-
-The result of any transactions, including deployments, will appear in the Remix terminal. Click the chevron next to the blue _Debug_ button to expand the log.
-
-
-
-
-
-Doing so will show the full transaction log, which contains all of the details of the transaction, such as its amount, the address to and from, and the inputs and outputs provided to the transaction.
-
-In this case, the sender (from) matches the first listed account in the panel, which has spent a small amount of simulated Ether to deploy the contract.
-
-You can access a list of 15 test wallets here, each with 100 Ether to spend. Among other uses, you can use these accounts to compare behavior between wallets that are and are not the owner of a deployed contract.
-
-### Interacting with the Contract
-
-Click the chevron to expand your contract in the Deployed Contracts section of the left panel. You'll see two buttons, one for each `public` function in the `Storage` contract. Notice how the _Store_ button also has a field to pass a _uint256_, matching the parameter for `uint256 num`.
-
-
-
-
-
-Let's click the retrieve button first. Before clicking, make a prediction: given that the `number` variable was instantiated without a value, what do you think the return will be?
-
-Go ahead and click – the result will appear below the button as:
-
-```text
-0: uint256: 0
-```
-
-
-
-
-
-Outputs from the EVM are in the form of an array, so in this case, the only return is in the 0th element and it is a `uint256` of 0. Were you expecting `undefined` or an error?
-
-Unlike many languages, variables in Solidity have a [default value] if not assigned. For `uint` and `int`, that value is 0.
-
-You can also review the results of your transaction in the console.
-
-
-
-
-
-The screenshot above is from a newer version of Remix than the video. Outputs are now often decoded for you!
-
-### Storing and Retrieving a Value
-
-Use the input to store and retrieve a value. Which costs more gas? Storing or retrieving? This isn't a trick question, but it is a bit nuanced. Both cost about 23500 gas, but there is only a gas cost for the retrieve function if it is called by another contract. Calling it from the web is free, because you're only reading data that is on the blockchain and are not asking the EVM to perform a computational task.
-
----
-
-## Disabling Artifact Generation
-
-Return to the _File Explorer_ by clicking the double document icon in the upper left. You should now see a folder called _artifacts_ that has been added to your project. This folder contains a number of build artifacts, such as the [_ABI_] for your contract, that will be useful to you later, but currently just cause clutter.
-
-You can disable artifact generation by clicking the settings gear in the bottom left corner, then deselecting the first checkbox to _Generate contract metadata..._
-
-
-
-
-
----
-
-## Conclusion
-
-Remix makes it easy to write, deploy, and test contracts. Contracts are deployed by a wallet address to their own address. These addresses are similar to how IP addresses work, in that they enable connections across the network. You can test deployed contracts directly in Remix and use the console to see detailed information about each transaction.
-
----
-
-
-[default value]: https://docs.soliditylang.org/en/v0.8.17/control-structures.html#scoping-and-declarations
-[_ABI_]: https://docs.soliditylang.org/en/v0.8.13/abi-spec.html
diff --git a/docs/learn/introduction-to-solidity/introduction-to-remix-vid.mdx b/docs/learn/introduction-to-solidity/introduction-to-remix-vid.mdx
deleted file mode 100644
index 753d77ed5..000000000
--- a/docs/learn/introduction-to-solidity/introduction-to-remix-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Introduction to Remix
-description: Learn about the Remix online IDE.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/introduction-to-solidity/introduction-to-remix.mdx b/docs/learn/introduction-to-solidity/introduction-to-remix.mdx
deleted file mode 100644
index 30f4c974b..000000000
--- a/docs/learn/introduction-to-solidity/introduction-to-remix.mdx
+++ /dev/null
@@ -1,93 +0,0 @@
----
-title: 'Introduction to Remix'
-description: An introduction to the Remix online IDE.
-sidebarTitle: Remix Guide
-hide_table_of_contents: false
----
-
-In this lesson, you'll be introduced to an online Solidity IDE called Remix. You'll tour the workspace and explore a sample smart contract.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- List the features, pros, and cons of using Remix as an IDE
-- Deploy and test the Storage.sol demo contract in Remix
-
----
-
-## Remix Window Overview
-
-Begin by opening a browser window and navigating to [remix.ethereum.org]. Open the project you created and cleaned up at the end of the last reading, and open `1_Storage.sol`. The editor should be organized in a way that is familiar to you. It is divided into three areas:
-
-- Editor Pane
-- Terminal/Output
-- Left Panel
-
-### Editor Pane
-
-The editor pane loads with the Remix home screen, which contains news, helpful links, and warnings about common scams. Double-click on `1_Storage.sol` to open it in the editor. You can close the home tab if you'd like.
-
-
-
-
-
-You'll edit your code in the editor pane. It also has most of the features you're expecting, such as syntax and error highlighting. Note that in Remix, errors are not underlined. Instead, you'll see an❗to the left of the line number where the error is present.
-
-At the top, you'll see a large green arrow similar to the _Run_ button in other editors. In Solidity, this compiles your code, but it does not run it because you must first deploy your code to the simulated blockchain.
-
-### Terminal/Output
-
-Below the editor pane, you'll find the terminal:
-
-
-
-
-
-You'll primarily use this panel to observe transaction logs from your smart contracts. It's also one way to access Remix's very powerful debugging tools.
-
-### Left Panel
-
-As with many other editors, the left panel in Remix has a number of vertical tabs that allow you to switch between different tools and functions. You can explore the files in your current workspace, create and switch between workspaces, search your code, and access a number of plugins.
-
----
-
-## Plugins
-
-Most of the features in Remix are plugins and the ones you'll use the most are active by default. You can view and manage plugins by clicking the plug button in the lower-left corner, right above the settings gear. You can turn them off and on by clicking activate/deactivate, and some, such as the _Debug_ plugin will be automatically activated through other parts of the editor.
-
-### Solidity Compiler
-
-The first default plugin (after the search function) is the _Solidity Compiler_. Be sure to check the `Auto compile` option. Smart contracts are almost always very small files, so this shouldn't ever cause a performance problem while editing code.
-
-The `Compile and Run script` button in this plugin is a little misleading. This is **not** how you will usually run your contract through testing. You can click the `I` button for more information on this feature.
-
-Finally, if you have errors in your contracts, the complete text for each error will appear at the bottom of the pane. Try it out by introducing some typos to `1_Storage.sol`.
-
-### Deploy & Run Transactions
-
-The _Deploy & Run Transactions_ plugin is what you'll use to deploy your contracts and then interact with them. At the top are controls to select which virtual machine to use, mock user wallets with test Ether, and a drop-down menu to select the contract you wish to deploy and test.
-
-Fix any errors you introduced to `1_Storage.sol` and then click the orange `Deploy` button. You'll see your contract appear below as _STORAGE AT \_.
-
-
-There are two common gotchas that can be very confusing when deploying contracts in Remix.
-
-1. Each time you hit the Deploy button, a new copy of your contract is deployed but the previous deployments remain. Unless you are comparing or debugging between different versions of a contract, or deploying multiple contracts at once, you should click the `Trash` button to erase old deployments before deploying again.
-1. If your code will not compile, **clicking the deploy button will not generate an error!** Instead, the last compiled version will be deployed. Visually check and confirm that there are no errors indicated by a number in a red circle on top of the Compiler plugin.
-
-
-
----
-
-## Conclusion
-
-Remix is a robust editor with many features and one or two gotchas. It is an excellent tool to use at the beginning of your journey because you can jump right in and start writing code for smart contracts.
-
-## See also
-
-[Remix](https://remix.ethereum.org)
-
-[remix.ethereum.org]: https://remix.ethereum.org
diff --git a/docs/learn/introduction-to-solidity/introduction-to-solidity-overview.mdx b/docs/learn/introduction-to-solidity/introduction-to-solidity-overview.mdx
deleted file mode 100644
index 7eee3161e..000000000
--- a/docs/learn/introduction-to-solidity/introduction-to-solidity-overview.mdx
+++ /dev/null
@@ -1,77 +0,0 @@
----
-title: 'Overview'
-description: An overview of this module.
-hide_table_of_contents: false
----
-
-The course you are about to begin is designed to rapidly and thoroughly teach web3 concepts and language to web2 developers. It specifically highlights similarities and differences found in web3 vs. web2 and contains background information, guided coding practices, and independent exercises.
-
-This program is **not** suitable for people who are new to programming in general. While the explanations are thorough, they often rely on an expectation that you are familiar with the underlying concepts. We will not teach you what arrays are and how they are used, but we will show you how they work in this environment.
-
-## Prerequisites
-
-Before these lessons, you should:
-
-- Have several years of experience as a programmer in an object-oriented language
-- Be familiar with the uses and properties of the Ethereum blockchain and the EVM
-- Ideally, be familiar with at least one [curly-bracket] programming language
-
----
-
-## Objectives
-
-By the end of this module, you should be able to:
-
-- **Introduction to Solidity**
- - Describe why languages like Solidity are used to write smart contracts
- - Relate an overview of the history (and pace of change) of Solidity and its strengths and weaknesses
- - Deploy and test the Storage.sol demo contract in Remix
-- **Contracts and Basic Functions**
- - Construct a simple ""Hello World"" contract
- - Categorize basic data types
- - List the major differences between data types in Solidity as compared to other languages
- - Compare and contrast signed and unsigned integers
- - Write a pure function that accepts argument and returns a value
-- **Deploying Smart Contracts to a Testnet**
- - Describe the uses and properties of the Ethereum testnet
- - Compare and contrast Ropsten, Rinkeby, Goerli, and Sepolia
- - Deploy a contract to the Sepolia testnet and interact with it in Etherscan
-- **Control Structures**
- - Control code flow with if, else, while, and for
- - List the unique constraints for control flow in Solidity
-- **Storage in Solidity**
- - Diagram how a contract's data is stored on the blockchain (Contract -> Blockchain)
- - Order variable declarations to use storage efficiently
- - Diagram how variables in a contract are stored (Variable -> Contract)
-- **Arrays in Solidity**
- - Construct then store and retrieve values in storage and memory arrays
- - Describe the difference between storage and memory arrays
- - Diagram how arrays are stored
- - Write a function that can return a filtered subset of an array
-- **The Mapping Type**
- - Construct a Map (dictionary) data type
- - Diagram the storage of the Mapping data type
- - Recall that assignment of the Map data type is not as flexible as for other data types/in other languages
- - Restrict function calls with the `msg.sender` global variable
- - Recall that there is no collision protection in the EVM and why this (probably) ok
-- **Advanced Functions**
- - Describe how pure and view functions are different than functions that modify storage
- - Categorize functions as public, private, internal, or external based on their usage
- - Use modifiers to efficiently add functionality to multiple functions
- - Utilize require to write a function that can only be used when a variable is set to 'True'
-- **Structs**
- - Construct a struct (user-defined type) that contains several different data types
- - Declare members of the struct to maximize storage efficiency
- - Describe constraints related to assignment of structs depending on the types they contain
-- **Inheritance**
- - Write a smart contract that inherits from another contract
-- **Imports**
- - Import and use code from another file
-- **Errors**
- - Debug common solidity errors including execution reverted, out of gas, stack overflow, value overflow/underflow, index out of range, and so on
-- **New Keyword**
- - Write a contract that creates a new contract with the new keyword
-
----
-
-[curly-bracket]: https://en.wikipedia.org/wiki/List_of_programming_languages_by_type#Curly-bracket_languages
diff --git a/docs/learn/introduction-to-solidity/introduction-to-solidity-vid.mdx b/docs/learn/introduction-to-solidity/introduction-to-solidity-vid.mdx
deleted file mode 100644
index 9778d5bc2..000000000
--- a/docs/learn/introduction-to-solidity/introduction-to-solidity-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Introduction
-sidebarTitle: Video Tutorial
-description: Learn about the Solidity programming language.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/introduction-to-solidity/solidity-overview.mdx b/docs/learn/introduction-to-solidity/solidity-overview.mdx
deleted file mode 100644
index 2dc5cff50..000000000
--- a/docs/learn/introduction-to-solidity/solidity-overview.mdx
+++ /dev/null
@@ -1,168 +0,0 @@
----
-sidebarTitle: Overview
-title: 'Solidity Overview'
-description: An overview of the Solidity programming language.
-hide_table_of_contents: false
----
-
-import { Danger } from "/snippets/danger.mdx";
-
-In this article, you'll learn about the origins and history of Solidity, where to find the docs, and review some of the considerations that make programming in Solidity relatively unique. You'll also learn about how to get started with development!
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Describe why languages like Solidity are used to write smart contracts
-- Relate an overview of the history (and pace of change) of Solidity and its strengths and weaknesses
-
----
-
-## Introduction to Solidity
-
-Solidity is a high-level language used to develop smart contracts compatible with the Ethereum Virtual Machine. It is object-oriented, strongly typed, and allows variadic (more than one argument) returns. Solidity was [inspired] by a number of languages, particularly C++. Compared to other languages, Solidity changes very rapidly. Review the [releases] to see just how rapid!
-
-### The Docs
-
-The [Solidity Docs] are thorough and helpful. This guide will regularly reference them and they should be your first source for specific information related to any of the components in the language. As with any versioned doc source, always double-check that the version you're referencing matches the version you are developing with.
-
-### Origins TL;DR
-
-Solidity was developed by the Ethereum Project's Solidity team and was first previewed in 2014 at DevCon0. The original goal was to create an easy-to-use language for smart contract development. A great [history overview] can be found in the team's blog.
-
-### What it Actually Does
-
-Solidity is very similar to the programming languages you are familiar with in that it's a high-level language that is relatively human-readable, which is then compiled into byte-code that can be read by the EVM. For example, this:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.9;
-
-contract Hello {
- function HelloWorld() public pure returns (string memory) {
- return "Hello World!";
- }
-}
-```
-
-compiles into this:
-
-```text
-0x608060405234801561001057600080fd5b50610173806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80637fffb7bd14610030575b600080fd5b61003861004e565b604051610045919061011b565b60405180910390f35b60606040518060400160405280600c81526020017f48656c6c6f20576f726c64210000000000000000000000000000000000000000815250905090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100c55780820151818401526020810190506100aa565b60008484015250505050565b6000601f19601f8301169050919050565b60006100ed8261008b565b6100f78185610096565b93506101078185602086016100a7565b610110816100d1565b840191505092915050565b6000602082019050818103600083015261013581846100e2565b90509291505056fea2646970667358221220575a1ec2ade1712a7a3a4e91cc5d83212207e4a5c70f5b2bc50079ee65ad29b364736f6c63430008110033
-```
-
-As you can see, the first example is a little easier to read!
-
----
-
-## Programming for Ethereum with Solidity
-
-On the surface, writing code for the EVM using Solidity isn't particularly different from other programming languages. You write code organized into functions, and those functions get executed when called, often accepting arguments and returning values. However, there are a number of unusual traits that will require you to think a little differently. Additionally, the EVM is a much smaller, slower, and less-powerful computer than a desktop, or even a mobile device.
-
-### Gas Fees
-
-Every single [operation] your code performs costs gas, which your users pay for. You're probably already well-versed in _[time complexity]_ and know how to get an operation down to _O(log(n))_, when you have no choice but to run something that is _O(2^n)_, and that sometimes, nested for-loops go brrrrr. These constraints and practices still apply, but in Solidity, every inefficiency directly costs your users money, which can make your app more expensive, and less appealing, than needed.
-
-When you were learning about _time complexity_, you probably heard the term _space complexity_ once, and then it was never mentioned again. This is because normally, computation is expensive, and storage is practically free. The opposite is true on the EVM. It costs a minimum of **20,000** gas to initialize a variable, and a minimum of **5,000** to change it. Meanwhile, the cost to add two numbers together is **3** gas. This means it is often much cheaper to repeatedly derive a value that is calculated from other values than it is to calculate it once and save it.
-
-You also have to be careful to write code with predictable execution paths. Each transaction is sent with a gas limit and which various frameworks, such as _ethers.js_, in order to do their best to estimate. If this estimate is wrong, the transaction will fail, but **it will still cost the gas used up until the point it failed!**
-
-### Contract Size Limit
-
-[EIP-170] introduced a compiled byte-code size limit of **24 KiB** (24,576 B) to Ethereum Smart Contracts. Read that sentence again, as you're probably not used to thinking in this small of a number!
-
-While there isn't an exact ratio of lines of code to compiled byte-code size, you're limited to deploying contracts that are approximately 300-500 lines of Solidity.
-
-Luckily, there are a few ways around this limitation. Contracts can expose their functions to be called by other contracts, although there is an additional cost. Using this, you can write a suite of contracts designed to work together, or even make use of contracts already deployed by others. You can also use more advanced solutions, such as [EIP-2535].
-
-### Stack Limit
-
-Programs written for computers or mobile devices often work with hundreds of variables at the same time. The EVM operates with a stack that can hold 1,024 values, but it can only access the top 16.
-
-There are many implications of this limit, but the one you'll run into most commonly is the "Stack too Deep" error because you're trying to work with too many variables at once.
-
-In Solidity/EVM, your functions are limited to a total of 16 variables that are input, output, or initialized by the function.
-
-### Permanence
-
-Once deployed, smart contracts are permanent and cannot be changed by anyone, **even their creator(s)!** It is literally not possible to edit them. If the creators of a contract discover a vulnerability, they can't do anything about it except withdraw the funds - if the contract allows them to!
-
-As a result, standard practice is to have a smart contract audited by an expert, before deployment.
-
-### Pace of Change
-
-Solidity files always start with a license and a version:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.17;
-```
-
-One of the reasons for this is that the pace of development for Solidity is **very** fast, and changes are not always backwards-compatible. As a result, the compiler needs to know which version to use when converting the Solidity code to byte-code.
-
-Review the [changelog] to see some of the recent additions and fixes.
-
----
-
-## Development Environments
-
-We'll be covering two tools that can be used to develop in Solidity.
-
-### Remix
-
-We'll start with [Remix], an online IDE similar to Codepen, Replit, or CodeSandbox. Remix is a great place to get started because it works out of the box, has a number of demo contracts, and has great debugging tools. More information can be found at the [Remix Project] website.
-
-
-
-
-
-
-**BE VERY CAREFUL** while using Remix, as it can also be used by scammers. Remix itself will warn you about this, so take heed! One common scam is for the scammer to convince you to paste and deploy code that is allegedly some sort of automated moneymaker, such as a staking tool, or a bot.
-
-If you paste and run code that you don't understand, you may lose all assets from your currently connected wallet. You should also be careful to always navigate directly to `remix.ethereum.org`. More experienced developers prefer to use static versions of Remix deployed to [IPFS], but be careful. There are also deployments that are compromised and used as a part of a scam!
-
-
-
-### Hardhat
-
-[Hardhat] is a development environment that allows you to develop and test Solidity on your local machine. It includes debugging and unit testing tools, and has an ecosystem of third-party-developed plugins that ease development and deployment. Among other things, these plugins can help you deploy contracts, see the size of your compiled byte-code, and even see unit test coverage.
-
-We'll introduce Hardhat and local development after the basics.
-
-## Remix Setup
-
-The next lesson will explore one of the demo contracts within [Remix]. Open it up and review the quickstart information if this is your first time on the site. Then, open or create a new workspace using the `Default` template.
-
-**Delete** everything except the contracts folder and the `1_Storage.sol` contract within that folder. You can also leave `.prettierrc.json` if you'd like.
-
-
-
-
-
----
-
-## Conclusion
-
-On the surface, Solidity is very similar to other programming languages; most developers won't struggle to write familiar operations. However, there are some critically important properties to keep in mind. Operations are much more expensive than in other environments, particularly storage. You can use most of the practices you are accustomed to, but you are limited to very small contract sizes and by the size of the stack. Finally, remember that you should always use a separate wallet for development. If you make a mistake, you could lose anything in it!
-
----
-
-## See also
-
-[Solidity Docs](https://docs.soliditylang.org/en/v0.8.17/)
-
-[inspired]: https://docs.soliditylang.org/en/v0.8.17/language-influences.html
-[releases]: https://github.com/ethereum/solidity/releases
-[Solidity Docs]: https://docs.soliditylang.org/en/v0.8.17/
-[history overview]: https://blog.soliditylang.org/2020/07/08/solidity-turns-5/
-[operation]: https://ethereum.org/en/developers/docs/evm/opcodes/
-[time complexity]: https://en.wikipedia.org/wiki/Time_complexity
-[EIP-170]: https://eips.ethereum.org/EIPS/eip-170
-[EIP-2535]: https://eips.ethereum.org/EIPS/eip-2535
-[changelog]: https://github.com/ethereum/solidity/blob/develop/Changelog.md
-[Remix]: https://remix.ethereum.org/
-[IPFS]: https://ipfs.tech/
-[Remix Project]: https://remix-project.org/
-[Hardhat]: https://hardhat.org/
diff --git a/docs/learn/llms-full.txt b/docs/learn/llms-full.txt
deleted file mode 100644
index 8c8e94325..000000000
--- a/docs/learn/llms-full.txt
+++ /dev/null
@@ -1,89 +0,0 @@
-# https://docs.base.org/learn/llms-full.txt
-
-## Learn — Deep Guide for LLMs
-
-> A structured curriculum for onchain development: Ethereum concepts, Solidity, app development, tooling, and token development, with hands‑on videos and exercises.
-
-### What you can do here
-- Learn Ethereum and Base fundamentals
-- Practice Solidity with guided lessons and exercises
-- Build frontends that read and write to contracts
-- Use Foundry and Hardhat for testing, deployment, and verification
-- Design and ship tokens (ERC‑20/721) and NFTs
-
-## Navigation (with brief descriptions)
-
-### Building Onchain
-- [Welcome](./welcome) — Orientation and learning path
-
-### Onchain Concepts
-- [Core Concepts](./onchain-concepts/core-concepts) — Fundamental ideas
-- [Understanding the Tech Stack](./onchain-concepts/understanding-the-onchain-tech-stack) — Layers and components
-- [Web2 vs Onchain (Wallets, Identity, Gas, Nodes, Frontend, Onramps, Social, AI)](./onchain-concepts/understanding-the-onchain-tech-stack) — Comparisons
-- [Development Flow](./onchain-concepts/development-flow) — From idea to prod
-
-### Introduction to Ethereum
-- [Introduction](./introduction-to-ethereum/introduction-to-ethereum-vid) — Video
-- [Developer Overview](./introduction-to-ethereum/ethereum-dev-overview-vid) — Video
-- [Applications](./introduction-to-ethereum/ethereum-applications) — Examples
-- [Gas Use](./introduction-to-ethereum/gas-use-in-eth-transactions) — Fees
-- [EVM Diagram](./introduction-to-ethereum/evm-diagram) — Architecture
-- [Guide to Base](./introduction-to-ethereum/guide-to-base) — Base specifics
-
-### Onchain App Development
-- [Frontend Setup](./onchain-app-development/frontend-setup/overview) — App stack
-- [Writing to Contracts](./onchain-app-development/writing-to-contracts/useWriteContract) — Mutations
-- [Reading & Displaying Data](./onchain-app-development/reading-and-displaying-data/useReadContract) — Queries
-- [Account Abstraction](./onchain-app-development/account-abstraction/gasless-transactions-with-paymaster) — Gasless tx
-- [Cross‑Chain Development](./onchain-app-development/cross-chain/bridge-tokens-with-layerzero) — Interop
-- [Finance](./onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds) — Data & funding
-- [Deploy with Fleek](./onchain-app-development/deploy-with-fleek) — Hosting
-
-### Smart Contract Development
-- [Intro to Solidity](./introduction-to-solidity/introduction-to-solidity-overview) — Overview
-- [Contracts & Basic Functions](./contracts-and-basic-functions/intro-to-contracts-vid) — Basics
-- [Deploying to a Testnet](./deployment-to-testnet/overview-of-test-networks-vid) — Testnets/verify
-- [Control Structures](./control-structures/standard-control-structures-vid) — Logic
-- [Storage](./storage/simple-storage-video) — Layout
-- [Arrays](./arrays/arrays-in-solidity-vid) — Arrays
-- [Mappings](./mappings/mappings-vid) — Mappings
-- [Advanced Functions](./advanced-functions/function-visibility-vid) — Visibility/modifiers
-- [Structs](./structs/structs-vid) — Data types
-- [Inheritance](./inheritance/inheritance-vid) — Inheritance
-- [Imports](./imports/imports-vid) — Imports
-- [Errors](./error-triage/error-triage-vid) — Debugging
-- [The new Keyword](./new-keyword/creating-a-new-contract-vid) — new/deploy
-- [Interfaces](./interfaces/intro-to-interfaces-vid) — Calls
-- [Events](./events/hardhat-events-sbs) — Logging
-- [Address & Payable](./address-and-payable/address-and-payable) — Transfers
-
-### Development with Foundry
-- [Deploy with Foundry](./foundry/deploy-with-foundry) — Setup/deploy
-- [Setup with Base](./foundry/setup-with-base) — Base specifics
-- [Testing Smart Contracts](./foundry/testing-smart-contracts) — Tests
-- [Verify with Basescan](./foundry/verify-contract-with-basescan) — Verify
-- [Random Numbers](./foundry/generate-random-numbers-contracts) — RNG
-
-### Development with Hardhat
-- [Setup & Overview](./hardhat/hardhat-setup-overview/hardhat-setup-overview-sbs) — Setup
-- [Testing with TypeScript](./hardhat/hardhat-testing/testing-overview-vid) — Tests
-- [Etherscan](./hardhat/etherscan/etherscan-sbs) — Verify
-- [Hardhat Deploy](./hardhat/hardhat-deploy/hardhat-deploy-sbs) — Deploy
-- [Verifying Contracts](./hardhat/hardhat-verify/hardhat-verify-vid) — Verify
-- [Mainnet Forking](./hardhat/hardhat-forking/mainnet-forking-vid) — Forking
-- [Tools & Testing](./hardhat/hardhat-tools-and-testing/overview) — Debug/coverage/gas/size
-
-### Token Development
-- [Intro to Tokens](./token-development/intro-to-tokens/tokens-overview) — Tokens
-- [Minimal Tokens](./token-development/minimal-tokens/minimal-token-sbs) — Minimal ERCs
-- [ERC‑20](./token-development/erc-20-token/erc-20-standard) — Fungible tokens
-- [ERC‑721](./token-development/erc-721-token/erc-721-standard) — NFTs
-- [NFT Guides](./token-development/nft-guides/signature-mint) — Minting/dynamic
-
-### Exercise Contracts
-- [Exercises](./exercise-contracts) — Practice contracts
-
-## Minimal Critical Code
-None — learning content. See product sections for runnable code.
-
-
diff --git a/docs/learn/llms.txt b/docs/learn/llms.txt
deleted file mode 100644
index 4747c4296..000000000
--- a/docs/learn/llms.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-# https://docs.base.org/learn/llms.txt
-
-## Learn Documentation
-
-> Structured curriculum for building onchain: Ethereum fundamentals, Solidity, app development, tools, and token development.
-
-## Building Onchain
-- [Welcome](https://docs.base.org/learn/welcome.md) — Program overview
-
-## Onchain Concepts
-- [Core Concepts](https://docs.base.org/learn/onchain-concepts/core-concepts.md) — Definitions and mental models
-- [Understanding the Onchain Tech Stack](https://docs.base.org/learn/onchain-concepts/understanding-the-onchain-tech-stack.md) — Layers and components
-- [Web2 vs Onchain (Wallets/Identity/Gas/Nodes/Frontend/Onramps/Social/AI)](https://docs.base.org/learn/onchain-concepts/understanding-the-onchain-tech-stack.md) — Comparative guides
-- [Development Flow](https://docs.base.org/learn/onchain-concepts/development-flow.md) — From idea to production
-
-## Introduction to Ethereum
-- [Introduction](https://docs.base.org/learn/introduction-to-ethereum/introduction-to-ethereum-vid.md) — Video overview
-- [Developer Overview](https://docs.base.org/learn/introduction-to-ethereum/ethereum-dev-overview-vid.md) — Video guide
-- [Applications](https://docs.base.org/learn/introduction-to-ethereum/ethereum-applications.md) — What’s possible
-- [Gas Use](https://docs.base.org/learn/introduction-to-ethereum/gas-use-in-eth-transactions.md) — Fees
-- [EVM Diagram](https://docs.base.org/learn/introduction-to-ethereum/evm-diagram.md) — Architecture
-- [Guide to Base](https://docs.base.org/learn/introduction-to-ethereum/guide-to-base.md) — Base specifics
-
-## Onchain App Development
-- [Frontend Setup](https://docs.base.org/learn/onchain-app-development/frontend-setup/overview.md) — App stack overview
-- [Writing to Contracts](https://docs.base.org/learn/onchain-app-development/writing-to-contracts/useWriteContract.md) — Mutations
-- [Reading & Displaying Data](https://docs.base.org/learn/onchain-app-development/reading-and-displaying-data/useReadContract.md) — Queries
-- [Account Abstraction](https://docs.base.org/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.md) — Gasless flows
-- [Cross‑Chain Development](https://docs.base.org/learn/onchain-app-development/cross-chain/bridge-tokens-with-layerzero.md) — Interop
-- [Finance](https://docs.base.org/learn/onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds.md) — Data and funding flows
-- [Deploy with Fleek](https://docs.base.org/learn/onchain-app-development/deploy-with-fleek.md) — Hosting
-
-## Smart Contract Development
-- [Intro to Solidity](https://docs.base.org/learn/introduction-to-solidity/introduction-to-solidity-overview.md) — Fundamentals and Remix
-- [Contracts & Basic Functions](https://docs.base.org/learn/contracts-and-basic-functions/intro-to-contracts-vid.md) — Basics
-- [Deploying to a Testnet](https://docs.base.org/learn/deployment-to-testnet/overview-of-test-networks-vid.md) — Testnets and verification
-- [Control Structures](https://docs.base.org/learn/control-structures/standard-control-structures-vid.md) — Logic
-- [Storage](https://docs.base.org/learn/storage/simple-storage-video.md) — Layout
-- [Arrays](https://docs.base.org/learn/arrays/arrays-in-solidity-vid.md) — Patterns
-- [Mappings](https://docs.base.org/learn/mappings/mappings-vid.md) — Key/value storage
-- [Advanced Functions](https://docs.base.org/learn/advanced-functions/function-visibility-vid.md) — Modifiers and visibility
-- [Structs](https://docs.base.org/learn/structs/structs-vid.md) — Data modeling
-- [Inheritance](https://docs.base.org/learn/inheritance/inheritance-vid.md) — Composition
-- [Imports](https://docs.base.org/learn/imports/imports-vid.md) — Reuse
-- [Errors](https://docs.base.org/learn/error-triage/error-triage-vid.md) — Debugging
-- [The new Keyword](https://docs.base.org/learn/new-keyword/creating-a-new-contract-vid.md) — Deploying new contracts
-- [Interfaces](https://docs.base.org/learn/interfaces/intro-to-interfaces-vid.md) — Contract calls
-- [Events](https://docs.base.org/learn/events/hardhat-events-sbs.md) — Logging
-- [Address & Payable](https://docs.base.org/learn/address-and-payable/address-and-payable.md) — Transfers
-
-## Development with Foundry
-- [Deploy with Foundry](https://docs.base.org/learn/foundry/deploy-with-foundry.md) — Setup and deploy
-- [Setup with Base](https://docs.base.org/learn/foundry/setup-with-base.md) — Base specifics
-- [Testing Smart Contracts](https://docs.base.org/learn/foundry/testing-smart-contracts.md) — Test flows
-- [Verify Contract with Basescan](https://docs.base.org/learn/foundry/verify-contract-with-basescan.md) — Verification
-- [Random Numbers](https://docs.base.org/learn/foundry/generate-random-numbers-contracts.md) — Patterns
-
-## Development with Hardhat
-- [Setup & Overview](https://docs.base.org/learn/hardhat/hardhat-setup-overview/hardhat-setup-overview-sbs.md) — Project setup
-- [Testing with TypeScript](https://docs.base.org/learn/hardhat/hardhat-testing/testing-overview-vid.md) — Tests
-- [Etherscan](https://docs.base.org/learn/hardhat/etherscan/etherscan-sbs.md) — Verification
-- [Hardhat Deploy](https://docs.base.org/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.md) — Deployments
-- [Verifying Contracts](https://docs.base.org/learn/hardhat/hardhat-verify/hardhat-verify-vid.md) — Verify
-- [Mainnet Forking](https://docs.base.org/learn/hardhat/hardhat-forking/mainnet-forking-vid.md) — Forking
-- [Tools & Testing](https://docs.base.org/learn/hardhat/hardhat-tools-and-testing/overview.md) — Debug, coverage, gas, size
-
-## Token Development
-- [Intro to Tokens](https://docs.base.org/learn/token-development/intro-to-tokens/tokens-overview.md) — Token types
-- [Minimal Tokens](https://docs.base.org/learn/token-development/minimal-tokens/minimal-token-sbs.md) — Minimal ERCs
-- [ERC‑20 Tokens](https://docs.base.org/learn/token-development/erc-20-token/erc-20-standard.md) — Fungible tokens
-- [ERC‑721 Tokens](https://docs.base.org/learn/token-development/erc-721-token/erc-721-standard.md) — NFTs
-- [NFT Guides](https://docs.base.org/learn/token-development/nft-guides/signature-mint.md) — Minting and dynamic NFTs
-
-## Optional
-- [Exercise Contracts](https://docs.base.org/learn/exercise-contracts.md) — Practice repo
-
diff --git a/docs/learn/mappings/how-mappings-are-stored-vid.mdx b/docs/learn/mappings/how-mappings-are-stored-vid.mdx
deleted file mode 100644
index fb4f9da60..000000000
--- a/docs/learn/mappings/how-mappings-are-stored-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: How Mappings are Stored
-description: Learn about `msg.sender`.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/mappings/mappings-exercise.mdx b/docs/learn/mappings/mappings-exercise.mdx
deleted file mode 100644
index f31b19f70..000000000
--- a/docs/learn/mappings/mappings-exercise.mdx
+++ /dev/null
@@ -1,74 +0,0 @@
----
-title: Mappings Exercise
-sidebarTitle: Exercise
-description: Exercise - Demonstrate your knowledge of mappings.
-hide_table_of_contents: false
----
-
-Create a contract that adheres to the following specifications.
-
----
-
-## Contract
-
-Create a single contract called `FavoriteRecords`. It should not inherit from any other contracts. It should have the following properties:
-
-### State Variables
-
-The contract should have the following state variables. It is **up to you** to decide if any supporting variables are useful.
-
-- A public mapping `approvedRecords`, which returns `true` if an album name has been added as described below, and `false` if it has not
-- A mapping called `userFavorites` that indexes user addresses to a mapping of `string` record names which returns `true` or `false`, depending if the user has marked that album as a favorite
-
-### Loading Approved Albums
-
-Using the method of your choice, load `approvedRecords` with the following:
-
-- Thriller
-- Back in Black
-- The Bodyguard
-- The Dark Side of the Moon
-- Their Greatest Hits (1971-1975)
-- Hotel California
-- Come On Over
-- Rumours
-- Saturday Night Fever
-
-### Get Approved Records
-
-Add a function called `getApprovedRecords`. This function should return a list of all of the names currently indexed in `approvedRecords`.
-
-### Add Record to Favorites
-
-Create a function called `addRecord` that accepts an album name as a parameter. **If** the album is on the approved list, add it to the list under the address of the sender. Otherwise, reject it with a custom error of `NotApproved` with the submitted name as an argument.
-
-### Users' Lists
-
-Write a function called `getUserFavorites` that retrieves the list of favorites for a provided `address memory`.
-
-### Reset My Favorites
-
-Add a function called `resetUserFavorites` that resets `userFavorites` for the sender.
-
----
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/mappings/mappings-sbs.mdx b/docs/learn/mappings/mappings-sbs.mdx
deleted file mode 100644
index cbb349382..000000000
--- a/docs/learn/mappings/mappings-sbs.mdx
+++ /dev/null
@@ -1,212 +0,0 @@
----
-title: Mappings
-sidebarTitle: Step by Step Guide
-description: Use the mapping data type to store key-value pairs.
-hide_table_of_contents: false
----
-
-In Solidity, the hashtable/hashmap/dictionary-comparable type used to store key-value pairs is called a `mapping`. `mapping`s are a powerful tool with many uses, but they also have some unexpected limitations. They also **aren't** actually hash tables!
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Construct a Map (dictionary) data type
-- Recall that assignment of the Map data type is not as flexible as for other data types/in other languages
-- Restrict function calls with the `msg.sender` global variable
-- Recall that there is no collision protection in the EVM and why this is (probably) ok
-
----
-
-## Mappings in Solidity vs. Hash Tables
-
-On the surface, the [`mapping`] data type appears to be just another hash table implementation that stores pairs of any hashable type as a key, to any other type as a value. The difference is in implementation.
-
-In a more traditional implementation, the data is stored in memory as an array, with a hash-to-index _(hashmod)_ function used to determine which spot in the array to store a given value, based on the key. Sometimes, the _hashmod_ function for two different keys results in the same index, causing a _collision_.
-
-Collisions are resolved via additional solutions, such as linked list chaining; when the underlying array starts to get full, a bigger one is created, all the keys are re-hash-modded, and all the values moved over to the new array.
-
-In the EVM, `mappings` do **not** have an array as the underlying data structure. Instead, the `keccak256` hash of the key plus the storage slot for the mapping itself is used to determine which storage slot out of all 2\*\*256 will be used for the value.
-
-There is no collision-handling, for the same reason that makes wallets work at all - 2\*\*256 is an unimaginably large number. One of the biggest numbers you might encounter regularly is the number of possible configurations for a [shuffled deck of cards], which is:
-
-80658175170943878571660636856403766975289505440883277824000000000000
-
-Meanwhile, the number of variations of a `keccak256` hash are:
-
-115792089237316195423570985008687907853269984665640564039457584007913129639935
-
-Collisions are very unlikely.
-
-As a result, there are a few special characteristics and limitations to keep in mind with the `mapping` data type:
-
-- Mappings can only have a data location of `storage`
-- They can't be used as parameters or returns of public functions
-- They are not iterable and you cannot retrieve a list of keys
-- All possible keys will return the default value, unless another value has been stored
-
-### Creating a Mapping
-
-Create a contract called `Mappings`. In it, add a `mapping` from an `address` to a `uint` called `favoriteNumbers`.
-
-
-
-```solidity
-contract Mappings {
- mapping (address => uint) favoriteNumbers;
-}
-```
-
-
-
-### Writing to the Mapping
-
-Add a function called `saveFavoriteNumber` that takes an `address` and `uint`, then saves the `uint` in the mapping, with the `address` as the key.
-
-
-
-```solidity
-function saveFavoriteNumber(address _address, uint _favorite) public {
- favoriteNumbers[_address] = _favorite;
-}
-```
-
-
-
-Deploy and test it out. Does it work? Probably...
-
-You don't have a way to read the data in `favoriteNumber`, but this problem is easy to correct. Similar to arrays, if you mark a `mapping` as public, the Solidity compiler will automatically create a getter for values in that `mapping`.
-
-Update the declaration of `favoriteNumbers` and deploy to test again.
-
-### Utilizing msg.sender
-
-Another issue with this contract is that a `public` function can be called by anyone and everyone with a wallet and funds to pay gas fees. As a result, anyone could go in after you and change your favorite number from lucky number **13** to anything, even **7**!
-
-That won't do at all!
-
-Luckily, you can make use of a [global variable] called `msg.sender` to access the `address` of the wallet that sent the transaction. Use this to make it so that only the owner of an `address` can set their favorite number.
-
-
-
-```solidity
-function saveFavoriteNumber(uint _favorite) public {
- favoriteNumbers[msg.sender] = _favorite;
-}
-```
-
-
-
-Deploy and test again. Success!
-
----
-
-## Retrieving All Favorite Numbers
-
-One challenging limitation of the `mapping` data type is that it is **not** iterable - you cannot loop through and manipulate or return **all** values in the `mapping`.
-
-At least not with any built-in features, but you can solve this on your own. A common practice in Solidity with this and similar problems is to use multiple variables or data types to store the right combination needed to address the issue.
-
-### Using a Helper Array
-
-For this problem, you can use a helper array to store a list of all the keys present in `favoriteNumbers`. Simply add the array, and push new keys to it when saving a new favorite number.
-
-
-
-```solidity
-contract Mappings {
- mapping (address => uint) public favoriteNumbers;
- address[] addressesOfFavs;
-
- function saveFavoriteNumber(uint _favorite) public {
- favoriteNumbers[msg.sender] = _favorite;
- // Imperfect solution, see below
- addressesOfFavs.push(msg.sender);
- }
-}
-```
-
-
-
-To return all of the favorite numbers, you can then iterate through `addressesOfFavs`, look up that addresses' favorite number, add it to a return array, and then return the array when you're done.
-
-
-
-
-```solidity
-function returnAllFavorites() public view returns (uint[] memory) {
- uint[] memory allFavorites = new uint[](addressesOfFavs.length);
-
- for(uint i = 0; i < allFavorites.length; i++) {
- allFavorites[i] = favoriteNumbers[addressesOfFavs[i]];
- }
-
- return allFavorites;
-}
-```
-
-
-
-On the surface, this solution works, but there is a problem: What happens if a user **updates** their favorite number? Their address will end up in the list twice, resulting in a doubled entry in the return.
-
-A solution here would be to check first if the `address` already has a number as a value in `favoriteNumbers`, and only push it to the array if not.
-
-
-
-```solidity
-function saveFavoriteNumber(uint _favorite) public {
- if(favoriteNumbers[msg.sender] == 0) {
- addressesOfFavs.push(msg.sender);
- }
- favoriteNumbers[msg.sender] = _favorite;
-}
-```
-
-
-
-You should end up with a contract similar to this:
-
-
-
-```solidity
-pragma solidity 0.8.17;
-
-contract Mappings {
- mapping (address => uint) public favoriteNumbers;
- address[] addressesOfFavs;
-
- function saveFavoriteNumber(uint _favorite) public {
- if(favoriteNumbers[msg.sender] == 0) {
- addressesOfFavs.push(msg.sender);
- }
- favoriteNumbers[msg.sender] = _favorite;
- }
-
- function returnAllFavorites() public view returns (uint[] memory) {
- uint[] memory allFavorites = new uint[](addressesOfFavs.length);
-
- for(uint i = 0; i < allFavorites.length; i++) {
- allFavorites[i] = favoriteNumbers[addressesOfFavs[i]];
- }
-
- return allFavorites;
- }
-}
-```
-
-
-
----
-
-## Conclusion
-
-In this lesson, you've learned how to use the `mapping` data type to store key-value pairs in Solidity. You've also explored one strategy for solving some of the limitations found in the `mapping` type when compared to similar types in other languages.
-
----
-
-[`mapping`]: https://docs.soliditylang.org/en/v0.8.17/types.html#mapping-types
-[hash table]: https://en.wikipedia.org/wiki/Hash_table
-[shuffled deck of cards]: https://czep.net/weblog/52cards.html
-[global variable]: https://docs.soliditylang.org/en/v0.8.17/units-and-global-variables.html
diff --git a/docs/learn/mappings/mappings-vid.mdx b/docs/learn/mappings/mappings-vid.mdx
deleted file mode 100644
index 238edede1..000000000
--- a/docs/learn/mappings/mappings-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Mappings
-sidebarTitle: Mappings Overview
-description: Learn about mappings.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/mappings/using-msg-sender-vid.mdx b/docs/learn/mappings/using-msg-sender-vid.mdx
deleted file mode 100644
index 63e627fad..000000000
--- a/docs/learn/mappings/using-msg-sender-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Using msg.sender
-description: Learn about `msg.sender`.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/new-keyword/creating-a-new-contract-vid.mdx b/docs/learn/new-keyword/creating-a-new-contract-vid.mdx
deleted file mode 100644
index e30fdc845..000000000
--- a/docs/learn/new-keyword/creating-a-new-contract-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Creating a new Contract
-sidebarTitle: Creating New Contracts
-description: Use the `new` keyword to create a contract that can create contracts.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/new-keyword/new-keyword-exercise.mdx b/docs/learn/new-keyword/new-keyword-exercise.mdx
deleted file mode 100644
index 28b366c17..000000000
--- a/docs/learn/new-keyword/new-keyword-exercise.mdx
+++ /dev/null
@@ -1,92 +0,0 @@
----
-title: New Exercise
-description: Exercise - Demonstrate your knowledge of the `new` keyword.
-sidebarTitle: Exercise
-hide_table_of_contents: false
----
-
-For this exercise, we're challenging you to build a solution requiring you to use a number of the concepts you've learned so far. Have fun and enjoy!
-
----
-
-## Contracts
-
-Build a contract that can deploy copies of an address book contract on demand, which allows users to add, remove, and view their contacts.
-
-You'll need to develop two contracts for this exercise and import **at least** one additional contract.
-
-## Imported Contracts
-
-Review the [Ownable](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol) contract from OpenZeppelin. You'll need to use it to solve this exercise.
-
-You may wish to use another familiar contract to help with this challenge.
-
-## AddressBook
-
-Create an `Ownable` contract called `AddressBook`. It includes:
-
-- A `struct` called `Contact` with properties for:
- - `id`
- - `firstName`
- - `lastName`
- - a `uint` array of `phoneNumbers`
-- Additional storage for `contacts`
-- Any other necessary state variables
-
-It should include the following functions:
-
-### Add Contact
-
-The `addContact` function should be usable only by the owner of the contract. It should take in the necessary arguments to add a given contact's information to `contacts`.
-
-### Delete Contact
-
-The `deleteContact` function should be usable only by the owner and should delete the contact under the supplied `_id` number.
-
-If the `_id` is not found, it should revert with an error called `ContactNotFound` with the supplied id number.
-
-### Get Contact
-
-The `getContact` function returns the contact information of the supplied `_id` number. It reverts to `ContactNotFound` if the contact isn't present.
-
-
-**Question**
-
-For bonus points (that only you will know about), explain why we can't just use the automatically generated getter for `contacts`?
-
-
-
-### Get All Contacts
-
-The `getAllContacts` function returns an array with all of the user's current, non-deleted contacts.
-
-
-You shouldn't use `onlyOwner` for the two _get_ functions. Doing so won't prevent a third party from accessing the information, because all information on the blockchain is public. However, it may give the mistaken impression that information is hidden, which could lead to a security incident.
-
-
-
-## AddressBookFactory
-
-The `AddressBookFactory` contains one function, `deploy`. It creates an instance of `AddressBook` and assigns the caller as the owner of that instance. It then returns the `address` of the newly-created contract.
-
----
-
-## Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-{/* */}
-
-
diff --git a/docs/learn/new-keyword/new-keyword-sbs.mdx b/docs/learn/new-keyword/new-keyword-sbs.mdx
deleted file mode 100644
index ee0cd9728..000000000
--- a/docs/learn/new-keyword/new-keyword-sbs.mdx
+++ /dev/null
@@ -1,116 +0,0 @@
----
-title: The New Keyword
-sidebarTitle: Step by Step Guide
-description: Learn to create a contract that creates other contracts.
-hide_table_of_contents: false
----
-
-You've seen the `new` keyword and used it to instantiate `memory` arrays with a size based on a variable. You can also use it to write a contract that [creates other contracts].
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Write a contract that creates a new contract with the new keyword
-
----
-
-## Creating a Simple Contract Factory
-
-A contract factory is a contract that creates other contracts. To start, let's create and interact with a very simple one. Create a new project in Remix and add a file called `ContractFactory.sol`.
-
-### Adding the Template
-
-Imagine you want to create a contract that can store its owner's name and complement it upon request. You can create this contract fairly easily.
-
-
-
-```solidity
-contract Complimenter {
- string public name;
-
- constructor(string memory _name) {
- name = _name;
- }
-
- function compliment() public view returns(string memory) {
- return string.concat("You look great today, ", name);
- }
-}
-```
-
-
-
-Deploy and test.
-
-### Creating a Factory
-
-The `Complimenter` contract is a huge success! People love how it makes them feel and you've got customers banging on the doors and windows. Awesome!
-
-The only problem is that it takes time and effort to manually deploy a new version of the contract for each customer. Luckily, there's a way to write a contract that will act as a self-service portal for your customers.
-
-Start by adding a contract called `ComplimenterFactory`. The Remix interface makes things easier if you leave the factory in the same file as `Complimenter`.
-
-Add a function called `CreateComplimenter` that is public, accepts a `string` called `_name`, and returns an `address`.
-
-Creating a new contract is simple: `new Complimenter(_name)`
-
-You can also save the return from that instantiation into a variable. This reference can be used to call public functions in the deployed contract, and can be cast to an address. We can use it to get an easy reference to find the copies made by the factory. The end result should look similar to:
-
-
-
-```solidity
-contract ComplimenterFactory {
- function CreateComplimenter(string memory _name) public returns (address) {
- Complimenter newContract = new Complimenter(_name);
- return address(newContract);
- }
-}
-```
-
-
-
-### Testing
-
-Clear the environment if you haven't already, then start by deploying `ComplimenterFactory`. You've been working hard and deserve nice things, so call `CreateComplimenter` with your name.
-
-In the terminal, the _decoded output_ will be the address of the new contract.
-
-```text
-{
- "0": "address: 0x9e0BC6DB02E5aF99b8868f0b732eb45c956B92dD"
-}
-```
-
-Copy **only** the address.
-
-Switch the _CONTRACT_ to be deployed to `Complimenter`, then paste the address you copied in the field next to the _At Address_ button which is below the _Deploy_ button.
-
-
-
-
-
-Click _At Address_ and the instance of `Complimenter` should appear below `ComplimenterFactory`. Test to confirm it works, then try deploying more instances with the factory.
-
-
-
-
-
-
-If the deployed contract appears, but is instead a broken copy of the factory, it's because you didn't change the contract in the _CONTRACT_ dropdown above the deploy button.
-
-Remix is trying to interact with `Complimenter` using the _ABI_ from the factory contract, which won't work.
-
-
-
----
-
-## Conclusion
-
-In this lesson, you learned how to deploy contracts from another contract by using the `new` keyword. You also learned that you look great today!
-
----
-
-[creates other contracts]: https://docs.soliditylang.org/en/v0.8.17/control-structures.html?#creating-contracts-via-new
diff --git a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-biconomy.mdx b/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-biconomy.mdx
deleted file mode 100644
index 1ebc1a566..000000000
--- a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-biconomy.mdx
+++ /dev/null
@@ -1,314 +0,0 @@
----
-title: Account Abstraction on Base using Biconomy
-slug: /account-abstraction-with-biconomy
-description: A tutorial that teaches how to implement Account Abstraction into a Base project using Biconomy paymasters, bundlers, and smart accounts.
-author: taycaldwell
----
-
-# Account Abstraction on Base using Biconomy
-
-This page will guide you through the process of implementing Account Abstraction in your Base projects using Biconomy paymasters, bundlers, and smart accounts.
-
-## Objectives
-
-By the end of this tutorial you should be able to do the following:
-
-- Set up a smart contract project for Base using [Foundry](https://book.getfoundry.sh/)
-- Set up a Next.js frontend project using `create next-app`
-- Setup user login and authentication using [Particle Network](https://particle.network/)
-- Setup a [Biconomy](https://biconomy.io/) paymaster and bundler
-- Create a gasless transaction
-
-## Prerequisites
-
-### Foundry
-
-This tutorial requires you to have Foundry installed.
-
-- From the command-line (terminal), run: `curl -L https://foundry.paradigm.xyz | bash`
-- Then run `foundryup`, to install the latest (nightly) build of Foundry
-
-For more information, see the Foundry Book [installation guide](https://book.getfoundry.sh/getting-started/installation).
-
-### Coinbase Wallet
-
-This tutorial requires you to have a wallet. You can create a wallet by downloading the Coinbase Wallet browser extension:
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-To complete this tutorial, you will need to fund a wallet with ETH on Base Goerli.
-
-The ETH is required for covering gas fees associated with deploying smart contracts to the network.
-
-- To fund your wallet with ETH on Base Goerli, visit a faucet listed on the [Base Faucets](https://docs.base.org/base-chain/tools/network-faucets) page.
-
-## What is Biconomy?
-
-Biconomy is a toolkit that offers a full-stack solution for Account Abstraction, including smart accounts, paymasters for sponsoring gas fees, and bundlers for bundling user operations into a single transaction.
-
-### High-level concepts
-
-#### Account Abstraction
-
-Account Abstraction ([ERC-4337](https://eips.ethereum.org/EIPS/eip-4337)) allows users to use Smart Contract wallets instead of traditional Externally Owned Account (EOA) wallets.
-
-#### Smart Accounts
-
-A smart account (also known as a smart contract wallet) is a wallet that stores and manages digital assets (ERC-20 tokens, NFTs, etc.) using a smart contract.
-
-#### User Operations
-
-A user operation is a pseudo-transaction object sent by a smart account that describes a transaction to be sent. Multiple user operations are eventually bundled together and initiated as a single real transaction by a bundler.
-
-#### Paymaster
-
-A paymaster is a special smart contract that allows applications to "sponsor user operations", meaning it will pay for the gas fees associated with the resulting transaction.
-
-#### Bundler
-
-A special node that monitors a mempool of user operations and bundles multiple user operations into a single transaction.
-
-
-Biconomy's Paymaster is a service that allows you to sponsor gas fees for your users, enabling gasless transactions. Learn more in the [Biconomy documentation](https://docs.biconomy.io/).
-
-
-## Creating and deploying a smart contract
-
-Before you begin, you need to set up a smart contract development environment using Foundry.
-
-To create a new project, first create a new directory named `myproject`, and change it to your current working directory:
-
-```bash
-mkdir myproject
-cd myproject
-```
-
-### Creating a Foundry project
-
-Next, within the `myproject` directory create a new directory named `contracts`, and change it to your current working directory:
-
-```bash
-mkdir contracts
-cd contracts
-```
-
-Then create a new Foundry project by running the following command:
-
-```bash
-forge init
-```
-
-This will create a Foundry project with the following basic layout:
-
-```bash
-.
-├── foundry.toml
-├── script
-├── src
-└── test
-```
-
-
-The command creates a boilerplate Solidity smart contract file named `src/Counter.sol`. This is the primary contract you will use for this tutorial.
-
-
-### Compiling the smart contract
-
-Compile the smart contract to ensure it builds without any errors.
-
-To compile your smart contract, run:
-
-```bash
-forge build
-```
-
-### Setting up a wallet as the deployer
-
-Before you can deploy your smart contract to various chains you will need to set up a wallet to be used as the deployer.
-
-To do so, you can use the [`cast wallet import`](https://book.getfoundry.sh/reference/cast/cast-wallet-import) command to import the private key of the wallet into Foundry's securely encrypted keystore:
-
-```bash
-cast wallet import deployer --interactive
-```
-
-After running the command above, you will be prompted to enter your private key, as well as a password for signing transactions.
-
-
-For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key). **It is critical that you do NOT commit this to a public repo.**
-
-
-To confirm that the wallet was imported as the `deployer` account in your Foundry project, run:
-
-```bash
-cast wallet list
-```
-
-### Deploying the smart contract
-
-To deploy the smart contract, you can use the `forge create` command. The command requires you to specify the smart contract you want to deploy, an RPC URL of the network you want to deploy to, and the account you want to deploy with.
-
-
-Your wallet must be funded with ETH on the Base Goerli testnet to cover the gas fees associated with the smart contract deployment. Otherwise, the deployment will fail.
-
-To get testnet ETH, see the [prerequisites](#prerequisites).
-
-
-To deploy the smart contract to the Base Goerli testnet, run the following command:
-
-```bash
-forge create ./src/Counter.sol:Counter --rpc-url https://goerli.base.org --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-After running the command above, the contract will be deployed on the Base Goerli test network. You can view the deployment status and contract by using a [block explorer](/base-chain/tools/block-explorers).
-
-## Setting up the Paymaster and Bundler
-
-To setup the paymaster and bundler for your project, you will need to visit the [Biconomy Dashboard](https://dashboard.biconomy.io/) and complete the following steps.
-
-### Registering a paymaster
-
-Add and register a Paymaster by completing the following steps:
-
-1. Visit the sign in to the [Biconomy Dashboard](https://dashboard.biconomy.io/)
-1. From the dashboard, select the **Paymasters** tab and click **Add your first Paymaster**
-1. Provide a **Name** for your paymaster
-1. Select **Base Goerli** from the **Network** dropdown
-1. Click **Register**
-
-You should now have a registered Biconomy paymaster.
-
-
-The **API Key** and **Paymaster URL** for the paymaster are provided under the **Overview** tab in the [Biconomy Dashboard](https://dashboard.biconomy.io/).
-
-
-### Setting up the paymaster gas tank
-
-Set up and fund the paymaster's gas tank by completing the following steps:
-
-1. From the [dashboard](https://dashboard.biconomy.io/), navigate to the **Paymasters** tab
-1. Click **Setup gas tank** on the paymaster
-1. Navigate to **Gas-Tank > Deposit**, and click **Set up gas tank**
-1. Sign the message with your connected wallet to set up the gas tank
-1. Click **Go to deposit**
-1. Enter the amount of ETH you wish to deposit
-1. Click **Deposit**
-
-ETH should now be deposited into the gas tank for your paymaster. You can visit the Withdraw tab at a later time if you wish to withdraw the funds.
-
-### Setting up the paymaster policies
-
-Set up and fund the paymaster's gas tank by completing the following steps:
-
-1. From the [dashboard](https://dashboard.biconomy.io/), navigate to the **Paymasters** tab
-1. Select the paymaster to configure
-1. Navigate to **Policies > Contracts**, and click **Add your first contract**
-1. Add the **Name** and the **Smart contract address** for your contract
-1. Select the **increment** and **setNumber** write methods as methods to sponsor
-1. Click **Add Smart Contract**
-
-### Setting up a bundler
-
-1. Visit the sign in to the [Biconomy Dashboard](https://dashboard.biconomy.io/)
-1. From the dashboard, select the **Bundlers** tab
-
-
-At the time of writing this tutorial, the Bundler service is still under development, however a **Bundler URL** is provided for testing out UserOperations on test networks. You can specify the chain ID **84531** to use the Bundler URL on **Base Goerli** testnet.
-
-
-## Setting up the frontend
-
-### Creating a Next.js project
-
-After you set up your paymaster and bundler from the Biconomy Dashboard, the next step is to create a Next.js project for your app's frontend.
-
-From the root of the `myproject` directory of your project, create a new Next.js project by running the following command:
-
-```bash
-yarn create next-app
-cd my-app
-```
-
-### Installing the dependencies
-
-To use the paymaster and bundler that were setup from the Biconomy Dashboard, you will need to add a few dependencies to your Next.js project.
-
-To install Biconomy as a dependency to your project, run the following command:
-
-```bash
-yarn add @biconomy/account @biconomy/bundler @biconomy/common @biconomy/core-types @biconomy/paymaster ethers@5.7.2
-```
-
-Creating Biconomy smart accounts requires a signer from an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) provider. Biconomy works with a variety of different social login and embedded wallet onboarding solutions that provide access to a signer that can be used for creating smart accounts. In this tutorial, you will use [Particle Network](https://particle.network/) for user authentication and getting a smart account signer.
-
-To install Particle Network as a dependency to your project, run the following command:
-
-```bash
-yarn add @biconomy/particle-auth
-```
-
-### Updating the boilerplate code
-
-The main page (`page.tsx`) of the Next.js project created when running the `yarn create next-app` command contains a `Home` component. This component comes with a lot of code that is unnecessary for this tutorial.
-
-Replace the content of the `page.tsx` file with the following simplified code:
-
-```javascript
-'use client';
-
-import styles from './page.module.css';
-
-export default function Home() {
- return (
-
-
-
- );
-}
-```
-
-## Adding social login using Particle Network
-
-### Setting up Particle Network
-
-To get started adding social login into the app using Particle Network, import and initialize the Biconomy Particle Auth module in the `page.tsx` file as shown below:
-
-```javascript
-'use client';
-
-import styles from './page.module.css';
-import { ParticleAuthModule, ParticleProvider } from '@biconomy/particle-auth';
-
-const PARTICLE_PROJECT_ID = 'YOUR_PARTICLE_PROJECT_ID';
-const PARTICLE_CLIENT_ID = 'YOUR_PARTICLE_CLIENT_ID';
-const PARTICLE_APP_ID = 'YOUR_PARTICLE_APP_ID';
-
-const particle = new ParticleAuthModule.ParticleNetwork({
- projectId: PARTICLE_PROJECT_ID,
- clientKey: PARTICLE_CLIENT_ID,
- appId: PARTICLE_APP_ID,
- wallet: {
- displayWalletEntry: true,
- },
-});
-
-export default function Home() {
- return (
-
-
-
- );
-}
-```
-
-
-You will need to sign up for a Particle Network account and replace the values of `PARTICLE_PROJECT_ID`, `PARTICLE_CLIENT_ID`, and `PARTICLE_APP_ID`.
-
-
-
-If you encounter issues with Paymaster integration, ensure your API keys are correct and your contract is whitelisted in the Biconomy dashboard.
-
diff --git a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-particle-network.mdx b/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-particle-network.mdx
deleted file mode 100644
index 0bc5687bc..000000000
--- a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-particle-network.mdx
+++ /dev/null
@@ -1,587 +0,0 @@
----
-title: Account Abstraction on Base using Particle Network
-description: A walkthrough on Particle Network's Modular Smart Wallet-as-a-Service, leveraging account abstraction and social logins across various providers.
-authors:
- - TABASCOatw
----
-
-# Account Abstraction on Base using Particle Network
-
-Particle Network is a Smart Wallet-as-a-Service provider on Base, providing a modular Account Abstraction stack, allowing developers to use a variety of Paymasters, Bundlers, or smart accounts along with social logins.
-
-This document will guide you through the process of using Particle Network within your Base application, building a simple React project using `create-react-app`, Particle Auth Core, and Particle's AA SDK.
-
-## Objectives
-
-By the end of this guide, you should be able to:
-
-- Use Particle Auth Core to generate an Externally-Owned-Account (EOA) via a social login
-- Assign a chosen smart account to the EOA generated by Particle Auth Core
-- Set up a Bundler and Paymaster
-- Construct and execute a gasless transaction
-
-## Prerequisites
-
-### Wallet funds
-
-This guide requires you to have ETH on Base Sepolia, which will be used to showcase the execution of a gasless burn transaction.
-
-- To fund your wallet with ETH on Base Sepolia, visit one of the faucets listed on the [Base Faucets](https://docs.base.org/base-chain/tools/network-faucets) page.
-
-### Familiarity with modern, frontend web development
-
-In this example, you'll be building a React-based application using [create-react-app]. It's recommended that you have some level of familiarity with the basics of working with React.
-
-## Understanding Particle Network
-
-### Wallet-as-a-Service
-
-Particle Network provides a large suite of SDKs centered around the reduction of account-based friction.
-
-In this case, "account-based friction" refers to barriers-to-entry that some Web3 users may face as they onboard into an application and begin managing a wallet.
-
-This friction, in the context of this guide, can be placed within two distinct categories:
-
-1. The login process. Often, decentralized applications that tend to be more consumer-facing prefer login flows that aren't dependent upon a user downloading and managing a traditional wallet, as this can be a pain point for some.
-2. The rigidity of standard accounts. Externally Owned Accounts, or EOAs, are often quite rigid in how they operate. They're secured by one key and limited to a strict range of functions, thus developers (and therefore users) are confined to relatively low-level interaction with applications.
-
-Wallet-as-a-Service (WaaS) aims to solve the first of these two points, the login process. WaaS solutions provide an alternative to standard wallets, typically allowing users to use applications through accounts generated by social logins (such as Google, email, or phone). The interfaces for interacting with these accounts are also often embedded within the applications, resulting in a consistent, application-specific experience.
-
-WaaS providers other than Particle Network include [Web3Auth], [Privy], and [Magic], among others.
-
-### Account Abstraction
-
-Particle Network also aims to tackle the second friction point described above: account flexibility.
-
-Account Abstraction refers to a transition away from standard account structures, EOAs, to smart accounts. Smart accounts are contracts that act as a wallet, providing users with an account that feels equivalent to an EOA but is intrinsically programmable (due to it being a smart contract) and thus more flexible.
-
-The most popular modern implementation of Account Abstraction is [ERC-4337], which enables Account Abstraction without any consensus-layer protocol changes. It does this through numerous components of supporting infrastructure, including a Bundler and Paymaster.
-
-Particle Network describes its Account Abstraction stack as modular, referring to cross-compatibility with any provider of Bundlers, Paymasters, or smart accounts). Particle Network's Account Abstraction SDK runs and uses its own Bundler and Paymaster, with built-in support for [Biconomy]'s Paymaster. However, Particle has made it simple to plug into [external infrastructure and components], such as Paymasters or Bundlers from providers like Stackup or Pimlico.
-
-#### Wallet-as-a-Service + Account Abstraction
-
-Leveraging Account Abstraction directly with Wallet-as-a-Service allows users to onboard through social logins into embedded wallets that use smart accounts, not EOAs, allowing for a greater degree of flexibility.
-
-Particle Network does this by allowing developers to use its Account Abstraction SDK alongside its Wallet-as-a-Service SDK (Particle Auth) to facilitate the intersection between both technologies, as we'll cover in this guide.
-
-
-To learn more about Account Abstraction and the concepts outlined above, see [ERC-4337], or Base's [Introduction to Account Abstraction] guide.
-
-
-## Setting up the frontend
-
-This guide will go through the process of creating a React-based application through the `create-react-app` template, as shown below.
-
-### Creating a React project
-
-To begin, we'll need to initialize a standard `create-react-app` project.
-
-Within your IDE of choice, run the following command, replacing `{name}` with the name of your project.
-
-```bash
-npx create-react-app {name}
-```
-
-This will create a React project within a directory under the name you set within `{name}`. This directory should contain the following structure by default:
-
-```bash
-{name}/
-├── node_modules/
-├── public/
-├── src/
-├── .gitignore
-├── package.json
-├── package-lock.json
-└── README.md
-```
-
-Throughout this guide, we'll primarily be working with the `src` folder and the files within it. `src` will contain the following by default:
-
-```bash
-src/
-├── App.css
-├── App.js
-├── App.test.js
-├── index.css
-├── index.js
-├── logo.svg
-└── reportWebVitals.js
-```
-
-This structure won't function properly out of the box as our example will be using JSX, a syntax extension that requires a special file type. Thus, you'll need to change the file extension of your `App.js` and `index.js` files to either `.jsx` (JavaScript) or `.tsx` (TypeScript).
-
-We will use these two files, `App.tsx` and `index.tsx`, within this guide.
-
-## Configuring & Initializing Particle Network
-
-### Installing the dependencies
-
-Before jumping into the application itself, it's important to walk through a few vital configurations required for Particle Network's SDKs to function properly.
-
-Within this example, we'll be using three core libraries from Particle, these include:
-
-- `@particle-network/auth-core-modal`, to directly initiate a social login and drive the usage of an embedded wallet.
-- `@particle-network/aa`, for configuring, assigning, and deploying a smart account.
-- `@particle-network/chain`, to allow Base to be used within this example.
-
-To add these as dependencies within your project, run one of the two following commands at the root of your project.
-
-```bash
-yarn add @particle-network/auth-core-modal @particle-network/aa @particle-network/chains
-
-# OR
-
-npm install @particle-network/auth-core-modal @particle-network/aa @particle-network/chains
-```
-
-In addition to the above libraries from `@particle-network`, we'll be using Ethers for core functions, such as retrieving the user's balance, sending a gasless transaction, and so on.
-
-For the sake of simplicity, we'll be using Ethers v5.7.2, the last release before the Ethers v6 upgrade. Although, if you'd like to use a newer version (v6), comments noting the new syntax of v6 will be left on the code snippets throughout this guide.
-
-Thus, you'll need to install Ethers through either of the following commands:
-
-```bash
-yarn add ethers@5.7.2
-
-# OR
-
-npm install ethers@5.7.2
-```
-
-### Setting up the Particle Network dashboard
-
-As you'll find in a moment, every library from Particle Network requires three key values for authentication. These are:
-
-1. Your `projectId`, assigned to a project created through the Particle dashboard.
-2. Your `clientKey`, similarly assigned to a project created through the dashboard, but serving a different purpose.
-3. Your `appId`, retrieved through the creation of an application (within a project) on the dashboard.
-
-Configuring both `@particle-network/auth-core-modal` and `@particle-network/aa` will require the retrieval and utilization of these three values.
-
-To create a project and an application through the Particle dashboard:
-
-1. Navigate to https://dashboard.particle.network.
-2. Create a new account (with your email).
-3. Click "Add New Project" and enter the name of your project.
-4. Copy and save the "Project ID" and "Client Key."
-5. Create a new application. For this example, we'll select "Web."
-6. Once again, enter the name of your project, alongside the domain where you intend to host this application. If you don't have a domain in mind, feel free to use any filler domain (such as 'base.org'), as it won't affect any underlying functionalities.
-7. Copy the "App ID" shown after creating an application.
-
-With these values retrieved, it's recommended that you assign them to environment variables within your application, such as `REACT_APP_PROJECT_ID`, `REACT_APP_CLIENT_KEY`, and `REACT_APP_APP_ID`.
-
-### Configuring Particle Auth Core
-
-Now that you've installed `@particle-network/auth-core-modal` (among other dependencies) and retrieved your project ID, client key, and app ID, you're ready to configure and therefore initialize the Particle Auth Core SDK.
-
-As mentioned, we'll be working out of two files within this guide:
-
-1. `App.jsx/tsx`, containing our core application logic (such as initiating social login and executing the transaction).
-2. `index.jsx/tsx`, used in this example for configuring `AuthCoreContextProvider` from `@particle-network/auth-core-modal`, the core configuration object for Particle Auth Core.
-
-`AuthCoreContextProvider` is a React component used to define these three aforementioned values, customize the embedded wallet modal and enable account abstraction within it. This will wrap our primary application component (`App` from `App.jsx/tsx`), therefore allowing Particle Auth Core to be used through various hooks within `App`.
-
-To achieve this, `AuthCoreContextProvider` will take the following parameters:
-
-- `projectId`, `clientKey`, and `appId`. These are the required values previously retrieved from the [Particle dashboard].
-- `erc4337`, used to define the type of smart account you intend to use, ensuring it's displayed and reflected within the embedded wallet interface.
-- `wallet`, for customizing the embedded wallet interface through the restriction of supported chains, color options, etc.
-
-With this in mind, an example of what your `index.jsx/tsx` file may look like given the usage of `AuthCoreContextProvider` has been included below.
-
-```tsx
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-
-import { AuthType } from '@particle-network/auth-core';
-import { BaseSepolia } from '@particle-network/chains';
-import { AuthCoreContextProvider } from '@particle-network/auth-core-modal';
-
-import App from './App';
-
-// Optional, needed for some environments
-import('buffer').then(({ Buffer }) => {
- window.Buffer = Buffer;
-});
-// -----
-
-ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
-
-
-
-
- ,
-);
-```
-
-## Setting Up Your Application
-
-### Importing and configuring hooks
-
-The second core component of this application is `App.jsx/tsx`, which will contain logic achieving the following:
-
-1. Configuration and assignment of a smart account (SimpleAccount in this example).
-2. Construction of a custom Ethers provider, using a custom AA-enabled EIP-1193 provider object.
-3. Initiation of social login.
-4. Execution of a gasless transaction.
-
-To achieve this, we'll be using a combination of hooks (from `@particle-network/auth-core-modal`) and base functions on Ethers, which will be automatically powered by AA through the custom EIP-1193 provider object.
-
-These hooks include `useEthereum` for the retrieval of the standard EOA-based provider object, `useConnect` for managing social logins, and `useAuthCore` to retrieve the user's information (after social login).
-
-To begin building `App.jsx/tsx`, you'll need to define the relevant functions from these hooks through a process similar to the example below:
-
-```tsx
-import React, { useState, useEffect } from 'react';
-import { useEthereum, useConnect, useAuthCore } from '@particle-network/auth-core-modal';
-import { BaseSepolia } from '@particle-network/chains';
-import { AAWrapProvider, SendTransactionMode, SmartAccount } from '@particle-network/aa';
-import { ethers } from 'ethers';
-
-import './App.css';
-
-const App = () => {
- // Standard, EOA-based 1193 provider
- const { provider } = useEthereum();
- // Used for initiating social login and disconnecting users (post-login)
- const { connect, disconnect } = useConnect();
- // Automatically loaded with relevant user information after logging in
- const { userInfo } = useAuthCore();
-};
-```
-
-## Configuring the Smart Account
-
-### Choosing and defining a smart account
-
-The EOA generated through the social login process will be used as the Signer for the smart account specified within this configuration, this will then be reflected through the embedded wallet modal (through its former selection within `AuthCoreContextProvider`).
-
-As mentioned, Particle Network supports a variety of smart accounts directly through its AA SDK, these include:
-
-- Light Account (by Alchemy).
-- Biconomy V1 and V2.
-- SimpleAccount (eth-infinitism).
-- CyberConnect.
-
-We'll be using SimpleAccount in this case. Although, note that you can change which smart account you use at any time through one line of code. This line of code exists on the `SmartAccount` object (imported from `@particle-network/aa`). `SmartAccount` acts as the central point for initializing a smart account.
-
-To configure `SmartAccount`, you'll be using the following parameters:
-
-- `projectId`, `clientKey`, and `appId`. These were used within `AuthCoreContextProvider` and can be retrieved from the [Particle dashboard] through the same procedure.
-- `aaOptions`, which contains `accountContracts`. Within `accountContracts`, you'll need to define a property corresponding with the smart account you'd like to use, i.e. `BICONOMY`, `LIGHT`, `CYBERCONNECT`, or `SIMPLE`.
- - This property contains `chainIds` and `version`.
-
-See the snippet below for an example of a constructed `SmartAccount` object:
-
-```tsx
-import React, { useState, useEffect } from 'react';
-import { useEthereum, useConnect, useAuthCore } from '@particle-network/auth-core-modal';
-import { BaseSepolia } from '@particle-network/chains';
-import { AAWrapProvider, SendTransactionMode, SmartAccount } from '@particle-network/aa';
-import { ethers } from 'ethers';
-
-import './App.css';
-
-const App = () => {
- ...
-
- const smartAccount = new SmartAccount(provider, {
- projectId: process.env.REACT_APP_PROJECT_ID,
- clientKey: process.env.REACT_APP_CLIENT_KEY,
- appId: process.env.REACT_APP_APP_ID,
- aaOptions: {
- accountContracts: {
- SIMPLE: [{ chainIds: [BaseSepolia.id], version: '1.0.0' }]
- // BICONOMY: [{ chainIds: [BaseSepolia.id], version: '1.0.0' }]
- // BICONOMY: [{ chainIds: [BaseSepolia.id], version: '2.0.0' }]
- // LIGHT: [{ chainIds: [BaseSepolia.id], version: '1.0.0' }]
- // CYBERCONNECT: [{ chainIds: [BaseSepolia.id], version: '1.0.0' }]
- }
- }
- });
-};
-```
-
-### Constructing a custom Ethers object
-
-There are two primary mechanisms to interact with the smart account defined previously. These are:
-
-1. Directly through the constructed `SmartAccount` object, such as with `{your object}.sendTransaction`.
-2. Through a Web3 library, such as Ethers or Web3.js. These libraries can facilitate interaction through a standardized provider object, allowing for a consistent syntax and setup through any wallet or account structure.
-
-As mentioned, we'll be using Ethers within this guide. You'll be able to use any typical Ethers method, such as `sendTransaction` or `getBalance` through the AA-enabled provider we construct with `AAWrapProvider` (imported from `@particle-network/aa`).
-
-Essentially, we'll construct an instance of `AAWrapProvider`, passing in the previously defined `SmartAccount` object alongside an object representing the method selected to pay gas fees. This will allow Ethers to directly load the smart account and drive transactions/signatures through Particle's embedded wallet.
-
-You can achieve this in one line of code, e.g.:
-
-```tsx
-// For Ethers V6, use ethers.BrowserProvider instead; the syntax below won't work.
-const customProvider = new ethers.providers.Web3Provider(
- new AAWrapProvider(smartAccount, SendTransactionMode.Gasless),
- 'any',
-);
-```
-
-`SendTransactionMode` has three options. They are:
-
-1. `SendTransactionMode.Gasless`, which will request gas sponsorship on every transaction sent through Ethers. By default, this will be through Particle's Omnichain Paymaster. If you don't have enough USDT deposited in the Paymaster to cover the gas fees, or if the transaction fails to meet your sponsorship conditions (set on the Particle dashboard), the user will pay the gas fees themselves.
-2. `SendTransactionMode.UserPaidNative`, the default method used if `SendTransactionMode` is missing from `AAWrapProvider`. This forces the user to pay gas fees themselves.
-3. `SendTransactionMode.UserSelect`, which allows a user to select which gas fee payment mechanism they use (ERC-20 token, native token, or request sponsorship).
-
-In this example, we're using `SendTransactionMode.Gasless`. Because this example uses Base Sepolia, all transactions will automatically be sponsored.
-
-## Initiating Social Login
-
-### Calling the connect function
-
-At this point, you've configured Particle Auth Core, initialized a smart account of choice, and constructed a custom Ethers provider to drive on-chain interaction.
-
-As covered earlier, one of the core benefits of Wallet-as-a-Service is the ability to onboard users through their social accounts. In this case, users' social login will directly create or log them into a smart account (if they have already created one through Particle Auth), allowing for the immediate usage of account abstraction.
-
-To initiate the social login process programmatically, you'll need to use the `connect` function, defined from the `useConnect` hook imported earlier (from `@particle-network/auth-core-modal`).
-
-Upon calling `connect`, a user will be brought through the social login process, after which an EOA will be generated (through MPC-TSS) and used as a signer for the smart account.
-
-`connect` takes the following parameters:
-
-- `socialType`, the specific social login mechanism you'd like users to go through. If left as an empty string, a generalized social login modal will be shown. Otherwise, use strings such as 'google', 'twitter', 'email', etc.
-- `chain`, an object (imported from `@particle-network/chains`) corresponding with the chain you'd like to use. In this example, it'll be `BaseSepolia`.
-
-Ideally, some logic should be set in place to ensure `connect` isn't called if a user has already logged in. This can be done by only calling `connect` on the condition that `userInfo` (from `useAuthCore`) is undefined, indicating that the user isn't logged in.
-
-Below is an example of calling `connect` (within the conditional described above).
-
-```tsx
-const handleLogin = async (authType) => {
- if (!userInfo) {
- await connect({
- socialType: authType,
- chain: BaseSepolia,
- });
- }
-};
-```
-
-In most applications, `connect` (or `handleLogin` in this example), will be bound to a "Login" or "Connect" button, as will be done here.
-
-## Executing a Gasless Transaction
-
-### Constructing a transaction
-
-Because we're using Ethers in this guide, constructing and executing a gasless transaction (intrinsically gasless through the previously defined `SendTransactionMode`) is identical to the flow you're likely already familiar with. However, it's important to note that transactions sent through ERC-4337 account abstraction do not follow standard transaction structures, these are called UserOperations.
-
-Typically, UserOperations follow lower-level, alternative structures. Although, through the usage of `AAWrapProvider`, the conversion between a simple transaction object (with `to`, `value`, `data`, etc.) to a complete UserOperation is handled automatically, allowing you to send transactions as you would normally.
-
-Thus, we'll be constructing a simple transaction (`tx`) adhering to the following structure:
-
-- `to`, the recipient address. For this example, we can burn a small amount of ETH on Base Sepolia, which means the recipient will be `0x000000000000000000000000000000000000dEaD`.
-- `value`, the value being sent in wei. Because of the default denomination in wei, this will be set as `ethers.utils.parseEther("0.001")`.
-
-If you intend on interacting with a contract, `data` can also be filled out (or within Ethers, a `Contract` object can be built).
-
-Therefore, your `tx` object should look like this:
-
-```tsx
-const executeUserOp = async () => {
- ...
-
- const tx = {
- to: "0x000000000000000000000000000000000000dEaD",
- value: ethers.utils.parseEther("0.001"),
- };
-
- ...
-};
-```
-
-### Executing a transaction
-
-Now that you've defined a standard transaction object, you'll need to execute it. Once again, due to the usage of Ethers, this is quite straightforward.
-
-We'll be using a `signer` object retrieved from `{your provider}.getSigner()` to call the `sendTransaction` method, which simply takes the `tx` object we constructed a moment ago.
-
-Upon calling `signer.sendTransaction(tx)`, the user will be prompted to confirm the transaction (sign a UserOperation hash) through an application-embedded popup. After doing so, the transaction will immediately be sent on Base Sepolia.
-
-To reflect the transaction hash after its confirmation on-chain, you can call the `wait` method on the variable you saved `signer.sendTransaction(tx)` to. The resulting object will contain a `transactionHash` value.
-
-See the example below for a visualization of this process:
-
-```tsx
-const executeUserOp = async () => {
- const signer = customProvider.getSigner();
-
- const tx = {
- to: '0x000000000000000000000000000000000000dEaD',
- value: ethers.utils.parseEther('0.001'),
- };
-
- const txResponse = await signer.sendTransaction(tx);
- const txReceipt = await txResponse.wait();
-
- return txReceipt.transactionHash;
-};
-```
-
-## Mapping to JSX
-
-You've now initiated social login (through `handleLogin`), assigned a smart account (through `SmartAccount`), and executed a gasless transaction (through `executeUserOp`).
-
-To present all of this to the user and allow them to interact with these functions for themselves, you'll need to map `handleLogin` and `executeUserOp` to the JSX of your `App` component. This will format the frontend that a user interacts with to test this application.
-
-Essentially, this displays either "Sign in with Google" or "Sign in with Twitter" through custom buttons that are only shown if the user hasn't logged in (determined through the state of `userInfo`). Upon logging in, the user can either call `executeUserOp` or `disconnect` (which was defined from `useConnect`).
-
-Below is an example of what your `App.jsx/tsx` file may look at this point. At the bottom of this snippet you'll find the JSX:
-
-```tsx
-import React, { useState, useEffect } from 'react';
-
-import { useEthereum, useConnect, useAuthCore } from '@particle-network/auth-core-modal';
-import { BaseSepolia } from '@particle-network/chains';
-import { AAWrapProvider, SmartAccount, SendTransactionMode } from '@particle-network/aa';
-
-import { ethers } from 'ethers';
-
-import './App.css';
-
-const App = () => {
- const { provider } = useEthereum();
- const { connect, disconnect } = useConnect();
- const { userInfo } = useAuthCore();
-
- // Initializes and assigns a smart account to the EOA resulting from social login
- const smartAccount = new SmartAccount(provider, {
- projectId: process.env.REACT_APP_PROJECT_ID,
- clientKey: process.env.REACT_APP_CLIENT_KEY,
- appId: process.env.REACT_APP_APP_ID,
- aaOptions: {
- accountContracts: {
- SIMPLE: [{ chainIds: [BaseSepolia.id], version: '1.0.0' }],
- },
- },
- });
-
- // Enables interaction with the smart account through Ethers
- const customProvider = new ethers.providers.Web3Provider(
- new AAWrapProvider(smartAccount, SendTransactionMode.Gasless),
- 'any',
- );
-
- // Initiates social login according to authType
- const handleLogin = async (authType) => {
- if (!userInfo) {
- await connect({
- socialType: authType,
- chain: BaseSepolia,
- });
- }
- };
-
- // Executes a gasless burn of 0.001 ETH
- const executeUserOp = async () => {
- const signer = customProvider.getSigner();
-
- const tx = {
- to: '0x000000000000000000000000000000000000dEaD',
- value: ethers.utils.parseEther('0.001'),
- };
-
- const txResponse = await signer.sendTransaction(tx);
- const txReceipt = await txResponse.wait();
-
- return txReceipt.transactionHash;
- };
-
- // The JSX
- return (
-
-
-
-
-
- {!userInfo ? (
-
-
-
-
- ) : (
-
-
{userInfo.name}
-
-
-
-
-
- )}
-
- );
-};
-
-export default App;
-```
-
-With everything complete, you're ready to run your application to test it. To do so, go through the following process:
-
-1. Navigate to the root of your project.
-2. Run either `npm run start` or `yarn start`.
-3. Once running, log in with either your Google or Twitter.
-4. Fund the address displayed on the wallet modal in the bottom right.
-5. Click "Execute User Operation".
-
-## Conclusion
-
-Congratulations! You've just built an application from scratch, onboarding users into smart accounts through social logins using Particle Network.
-
-To learn more about using Particle Network on Base, take a look at the following resources:
-
-- [Biconomy Guide (which uses Particle Network)]
-- [Account Abstraction on Base]
-- [Particle Network Documentation]
-- [Particle Network 101: Developer Experience]
-
-[create-react-app]: https://create-react-app.dev
-[Base Faucets]: https://docs.base.org/base-chain/tools/network-faucets
-[Web3Auth]: https://web3auth.io
-[Privy]: https://docs.base.org/tutorials/account-abstraction-with-privy-and-base-paymaster/
-[Magic]: https://magic.link
-[ERC-4337]: https://eips.ethereum.org/EIPS/eip-4337
-[Biconomy]: https://docs.base.org/guides/account-abstraction-with-biconomy
-[external infrastructure and components]: https://blog.particle.network/announcing-our-smart-wallet-as-a-service-modular-stack-upgrading-waas-with-erc-4337
-[Introduction to Account Abstraction]: https://docs.base.org/cookbook/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster
-[Particle dashboard]: https://dashboard.particle.network
-[Biconomy Guide (which uses Particle Network)]: https://docs.base.org/cookbook/account-abstraction/account-abstraction-on-base-using-biconomy
-[Account Abstraction on Base]: https://docs.base.org/tools/account-abstraction
-[Particle Network Documentation]: https://developers.particle.network
-[Particle Network 101: Developer Experience]: https://blog.particle.network/particle-network-101-developer-experience-edition
-
diff --git a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster.mdx b/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster.mdx
deleted file mode 100644
index 5f86a1aa9..000000000
--- a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster.mdx
+++ /dev/null
@@ -1,737 +0,0 @@
----
-title: 'Account Abstraction on Base using Privy and the Base Paymaster'
-slug: /account-abstraction-with-privy-and-base-paymaster
-description: A tutorial that teaches how to implement Account Abstraction into a Base project using Privy and the Base paymaster.
-author: Brian Doyle and Aaron Hayslip
----
-
-# Account Abstraction on Base using Privy and the Base Paymaster
-
-This tutorial shows you how to use [Privy], [Alchemy's Account Kit], and the [Base Paymaster] to enable your users to use onchain apps without creating a wallet on their own, or even needing to pay for gas fees!
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-### Intro to Account Abstraction
-
-- Explain how Account Abstraction can improve user experience for onchain apps
-- Describe the difference between contract accounts and EOAs, the limitations of contract accounts, and how EIP-4337 uses `UserOperation`s to mitigate these limitations
-- Outline how Account Abstraction works, and how users interact with smart contract wallets
-
-### Intro to Privy
-
-- Implement Privy's quick start to add onchain authentication to a NextJS application
-- Compare Privy's progressive authentication strategy with traditional wallet-based authentication
-- Use Privy's `PrivyProvider` context and `usePrivy` hook to implement basic authentication via an email address, SMS, EOA, and/or social auth
-- Utilize Privy's Embedded Wallets to enable users to utilize wallet-based actions without having to connect to an external wallet or leave your application
-
-### Implementing the Paymaster
-
-- Describe how a third party can use a _paymaster_ to sponsor gas
-- Modify Privy's Base [paymaster example] example to work in another app, using an EOA to allow a user to call a smart contract function without requiring the user to pay any gas
-
-## Prerequisites
-
-### 1. Be familiar with modern, frontend web development
-
-In this tutorial, we'll be working with a React frontend built with [Next.js]. While you don't need to be an expert, we'll assume that you're comfortable with the basics.
-
-### 2. Possess a general understanding of the EVM and smart contract development
-
-This tutorial assumes that you're reasonably comfortable writing basic smart contracts. If you're just getting started, jump over to our [Base Learn] guides and start learning!
-
-## Intro do Account Abstraction
-
-[ERC-4337], also known as _Account Abstraction_, is a standard that allows smart contracts to initiate transactions, thus enabling any logic that users want to implement to be encoded into the smart contract wallet itself for execution on Ethereum.
-
-Account Abstraction has the potential to be a massive game-changer for onchain user experience and many believe it will play a key role in bringing the next billion users onchain.
-
-### The Problem Account Abstraction Solves
-
-Onchain applications are difficult to use for many people, as they require a lengthy onboarding process for the average internet user who already onchain.
-
-For example,the preliminary steps a user needs to go through before they can use an onchain app are:
-
-1. Create a wallet (Coinbase Wallet, Metamask, etc)
-1. Store the wallet mnemonic safely, without losing it or compromising it
-1. Sign a slightly frightening message to connect to an onchain app
-1. Try to do anything with the app and get a popup asking them to approve a transaction
-1. Attempt to do so, learn transactions cost gas, and that they don't have any
-1. Learn that gas is a fee that users must pay in ETH to use onchain apps
-1. Attempt to buy ETH, possibly buying the wrong flavor of ETH in the wrong location
-1. Try the transaction again
-1. Repeat until they finally find the correct path
-
-For widespread adoption of onchain applications, this confusing and alienating process has to change.
-
-That's where Account Abstraction comes in. It allows you to improve the onboarding and usage flow for your users:
-
-1. User goes to the onchain app and authenticates (using email, EOA, or social auth)
-2. User uses the app
-3. Transactions happen under the hood via a smart contract wallet and the app developer sponsors the user's gas fees until after they're onboarded.
-
-To summarize, Account Abstraction enables smart contract accounts to initiate user operations, similar to how an EOA would initiate a transaction. However, unlike EOAs, smart contract accounts are programmable and can enable a number of incredible features, such as:
-
-- **Sponsored Transactions**: Allow application owners to cover the users gas fees with a paymaster or allow a user to use something other than ETH (USDC, for example) to cover gas
-- **Arbitrary Verification Logic**: Verify transactions with custom logic
-- **Account Recovery**: Create account recovery features for when user lose private keys
-- **Batching Transactions**: Change the user experience so that multiple transactions can be submitted at once
-
-### Overview of ERC-4337 - Account Abstraction
-
-"Account Abstraction" comes from [ERC-4337]. The proposal itself is a dense read, but one of stated goals of the proposal is to:
-
-> allow users to use smart contract wallets containing arbitrary verification logic instead of EOAs as their primary account. Completely remove any need at all for users to also have EOAs (as status quo SC wallets and EIP-3074 both require)
-
-In other words, the proposal seeks to allow users to use _smart contract wallets_ **instead** of EOAs to transact onchain.
-
-
-Privy enables seamless onboarding and wallet creation for users, abstracting away the complexity of private key management. Learn more in the [Privy documentation](https://docs.privy.io/).
-
-
-At first glance, you may be asking yourself if using smart contract accounts solve our problem. What's the difference between a smart contract account (or contract account) and an _Externally Owned Account_?
-
-According to [ethereum.org], the differences are:
-
-**Externally Owned Account**
-
-- Creating an account costs nothing
-- Can initiate transactions
-- Transactions between externally-owned accounts can only be ETH/token transfers
-- Made up of a cryptographic pair of keys: public and private keys that control account activities
-
-**Contract Accounts**
-
-- Creating a contract has a cost because you're using network storage
-- Can only send transactions in response to receiving a transaction
-- Transactions from an external account to a contract account can trigger code which can execute many different actions, such as transferring tokens or even creating a new contract
-- Contract accounts don't have private keys. Instead, they are controlled by the logic of the smart contract code
-
-Smart contract wallets _would_ solve our problems, **but**, as stated above, they can't initiate transactions. Since they cannot initiate transactions, users still need EOAs and those EOAs still need to pay for gas with ETH.
-
-There were two options to resolve this problem:
-
-1. Change the protocol - hard fork!
-
-2. Change transactions, upstream (ERC-4337)
-
-In a nutshell, ERC-4337 doesn't change how Ethereum transactions work. Cryptographically signed instructions from accounts still initiate transactions to update the state of the Ethereum network.
-
-What ERC-4337 changes is everything _upstream_ of that signed transaction. It does this by introducing a new _user intent layer_ that acts as a proxy for an EOA. This layer allows users to initiate transactions, with highly customizable smart contract wallets, but without using an EOA. The Ethereum network still receives what it received before - signed transactions.
-
-The result is a better experience for the user without changing the Ethereum protocol.
-
-### How Account Abstraction Works
-
-With typical Ethereum transactions an EOA initiates and signs a transaction. That transaction is sent to Ethereum's Public Mempool, is validated and added to a block, onchain.
-
-Note that the following steps are primarily happening **before** any of that.
-
-#### 1. **Smart Contract Wallet Creation**
-
-First, a new smart contract wallet must be created for a user. This wallet is owned by its creator who is designated as the _signer_. This signer must validate any of its future operations.
-
-This wallet may come with a variety of features, but it must be able to at least validate `UserOperations`s with a function called, `validateUserOp`. `validateUserOp` will check each `UserOperation`'s signature, increment the nonce, and handle the operation's fees.
-
-#### 2. **User Operation Creation**
-
-From the smart contract account, `UserOperations`s are created. These are not yet transactions, but rather represent intents from the user. These intents can represent any onchain user operation.
-
-`UserOperation` includes the details of the transaction such as sender, nonce, gas limit, max fee per gas, paymaster data (if applicable), and a signature.
-
-#### 3. **Signature Generation**:
-
-The `UserOperation` is then signed using the private key associated with the initiating account. This signature serves to authenticate the transaction and validate that it was indeed initiated by the owner of the smart contract account.
-
-#### 4. **Alt Mempool**:
-
-ERC-4337 introduces an _Alt Mempool_ where operations are stored until they're picked up by a _Bundler_. The Alt Mempool is not very different from the transaction pool typically used in Ethereum, but this mempool exists earlier on in the transaction and holds user operations, where Ethereum's mempool holds signed transactions.
-
-#### 5. **Bundler and Operation Submission**:
-
-Nodes on the Ethereum network have the option to serve as a _Bundler_, a role that involves collecting multiple signed `UserOperation`s and consolidating them into a single transaction, called a bundle transaction. These bundle transactions are then directed towards a universal smart contract, called the _EntryPoint_.
-
-The submission of the signed `UserOperation` to the EntryPoint contract can be done directly or through a _paymaster_, which is a contract that agrees to cover the cost of operations for certain users.
-
-#### 6. **Operation Validation**:
-
-The Bundler triggers a function named `handleOps` on the EntryPoint smart contract, which receives the bundle transaction. The EntryPoint then calls `validateUserOp` for each account within this bundle transaction.
-
-Each smart contract wallet is then required to implement an additional function and execute the actual operation sent by the EntryPoint contract.
-
-#### 7. **Operation Execution**:
-
-Once the operation has been validated and the fees have been handled, the operation is executed on the Ethereum network.
-
-## Intro to Privy
-
-[Privy] makes authentication and user-management in onchain apps easier by bridging onchain and offchain user data. In preparation to dive into our Base Paymaster example app, this lesson will cover the basics of Privy.
-
-In this tutorial, you'll quick start and review a sample application where users can authenticate with [Privy].
-
-### Overview of Privy
-
-Privy self-describes as "a simple toolkit for progressive authentication in web3". In this app, you'll primarily use Privy for its authentication and user-handling capabilities, but some of Privy's most popular features include:
-
-**Authentication Options**: Privy allows developers to configure how users authenticate. This can be with a crypto wallet, an email address, phone number or social profiles. Conveniently, Privy handles sessions and provides all necessary authentication methods.
-
-**Progressive Onboarding**: Privy creates a `user` object for each session. Before authentication, this `user` object is `null`, but as the user interacts with your application it will progressively associate more user information with this object. For example, users can start by authenticating with their email address and later add their wallet address or any other user information as the application requires.
-
-**Embedded Wallets**: Embedded wallets are "self-custodial Ethereum wallets that are embedded into your app. This allows your users to take wallet-based actions without ever leaving your site. Embedded wallets are the easiest way to unlock your full product experience for users who don't have, or don't want to connect, their own wallet." Developers can simply configure Privy to automatically created an Embedded Wallet on `login` or they can be pregenerated on the backend.
-
-
-Embedded wallets are still EOAs and should not be confused with _smart contract wallets_. A user may have both. During development, this division can lead to confusing situations where `msg.sender` is **not** the address you were expecting.
-
-
-### Privy Quick Start
-
-As with most onchain frontend connector libraries, you can use [Privy's Quick Start] to jump start your development with their platform. In this example, you'll use the NextJS starter.
-
-#### Setup
-
-First, navigate to the repository: [https://github.com/privy-io/create-next-app] and clone the repo.
-
-```bash
-git clone https://github.com/privy-io/create-next-app
-```
-
-Next, `cd create-next-app` and install dependencies with `yarn`.
-
-#### Setting Your App Id
-
-To use Privy, you'll need your own environment variables. In your terminal, run:
-
-```bash
-cp .env.example .env.local
-```
-
-This will create a `.env.local` file in your project's root. This is where you'll add your Privy App ID:
-
-```text
-NEXT_PUBLIC_PRIVY_APP_ID=
-```
-
-
-Note - to get an App ID, you'll need to request one and access it at [https://console.privy.io/]. This process can take a while, but for the Base community, Privy will expedite this process! Developers can send an email to `base@privy.io` with:
-
-- Your app name
-- The email address you want as admin
-- A one liner on what you're building
-
-
-
-#### Starting the App
-
-Finally, run `yarn dev` and navigate to [http://localhost:3000] to see the starter application.
-
-### Privy Login Walkthrough
-
-Before exploring the code, test the app. First, you should see this login page:
-
-
-
-
-
-After clicking "Log in" you'll see the following modal:
-
-
-
-
-
-By default, you can login with a wallet, or email.
-
-After logging in, you'll be redirected to the `/dashboard` page, where the demo app will allow you to connect a number of other accounts to your `user` object:
-
-
-
-
-
-If you navigate to [console.privy.io](https://console.privy.io/), you'll see that Privy stores all your users and their data here.
-
-
-
-
-
-### PrivyProvider
-
-Diving into the code, first look at the `PrivyProvider` inside of `_app.jsx`:
-
-```tsx
- router.push('/dashboard')}
->
-
-
-```
-
-`PrivyProvider` uses React Context and wraps any components that will use the `usePrivy` hook.
-
-Additionally, it's here that you can pass an optional `config` property to enable more authentication methods.
-
-Add a `config` property to the `` in `_app.jsx` with `'github'` and `'sms'` as the login options:
-
-```tsx
- router.push('/dashboard')}
- config={{
- loginMethods: ['github', 'sms'],
- }}
->
-
-
-```
-
-Refresh to see that authentication is only possible now through Github or SMS:
-
-
-
-
-
-You can find a full list of `loginMethods` in the docs for [`PrivyClientConfig`].
-
-### The `usePrivy` Hook
-
-The primary method you'll use to utilize Privy's authentication features is `usePrivy`. Open `pages/dashboard.tsx` to see the methods decomposed from `usePrivy` in the starter, and how they are used.
-
-A full list of the fields and methods returned from `usePrivy` are [documented here].
-
-### The `useWallets` Hook
-
-To access wallet data for currently authenticated user, use the `useWallets` hook:
-
-```tsx
-import { ConnectedWallet, useWallets } from '@privy-io/react-auth';
-
-const { wallets } = useWallets();
-
-// wallets = [
-// {
-// "address": "0x",
-// "type": "wallet",
-// "verifiedAt": "2023-11-28T19:01:41.000Z",
-// "chainType": "ethereum",
-// "chainId": "eip155:84531",
-// "walletClient": "unknown",
-// "walletClientType": "coinbase_wallet",
-// "connectorType": "coinbase_wallet"
-// },
-// {
-// "address": "0x",
-// "type": "wallet",
-// "verifiedAt": "2023-11-28T20:09:23.000Z",
-// "chainType": "ethereum",
-// "chainId": "eip155:1",
-// "walletClient": "unknown",
-// "walletClientType": "metamask",
-// "connectorType": "injected"
-// },
-// ],
-```
-
-As you can see, a user may connect multiple wallets to Privy, including Embedded Wallets.
-
-### Embedded Wallets
-
-Lastly, configure your starter app to create an Embedded Wallet for your users on login.
-
-As stated in the Privy docs,
-
-> Embedded wallets are self-custodial Ethereum wallets that are embedded into your app. This allows your users to take wallet-based actions without ever leaving your site. Embedded wallets are the easiest way to unlock your full product experience for users who don't have, or don't want to connect, their own wallet.
-
-When configuring your app to create embedded wallets on login, you have 2 options:
-
-- `users-without-wallets`: This will create embedded wallets for all use who did not login with an external wallet
-- `all-users`: This will create an additional embedded wallet for all users, regardless if they have linked an external wallet
-
-Inside of `_app.tsx`, update your `PrivyProvider`:
-
-```tsx
- router.push('/dashboard')}
- config={{
- embeddedWallets: {
- createOnLogin: 'all-users'
- }
- }}
->
-```
-
-Log out of your application, then log in again and see that your user has an additional `linkedAccount` which is the Privy Embedded Wallet:
-
-```tsx
-{
- "address": "0xD5063967BA703D485e3Ca40Ecd61882dfa5F49b2",
- "type": "wallet",
- "verifiedAt": "2023-11-28T20:52:22.000Z",
- "chainType": "ethereum",
- "chainId": "eip155:1",
- "walletClient": "privy",
- "walletClientType": "privy",
- "connectorType": "embedded",
- "recoveryMethod": "privy"
-},
-```
-
-More information on Privy's Embedded Wallets, including information about the addresses, signing transactions, funding the wallet and more, can be found here: [https://docs.privy.io/guide/frontend/embedded/overview]
-
-## Implementing the Paymaster
-
-A _paymaster_ is a type of smart contract account, introduced in [ERC-4337], that is able to pay for gas on behalf of another account. In this step-by-step, you'll modify an example created by [Privy], move it to another onchain app, and use it to call a smart contract function. Along the way, you'll encounter and resolve some of the confusing pitfalls associated with working with smart contract accounts.
-
-
-The tutorial below does not account for recent changes to the [Base Paymaster]. Please reference the linked repo and adjust. We'll update the tutorial soon!
-
-
-### Reviewing the Example
-
-Start by reviewing the [paymaster example]. The address in the `about` section of the Github page links to a deployed version of the app. It's the same app you get from [Privy's Quick Start], with the addition of a mint button (the versions may be a little older).
-
-The app is limited to social auth, so log in with either your Google account or email. You'll see the dashboard, with the addition of a `Mint NFT` button at the top.
-
-Click the button and you'll see a toast notification informing you of updates to the transaction status. Note that this happens **without** you needing to approve a transaction or fund a wallet!
-
-Click to see the transaction when it's done to open BaseScan. If you missed it, mint another NFT, it's not like you're paying gas!
-
-### Reviewing the Transaction and Contract
-
-The transaction page should appear fairly standard. You can see from it that an NFT was minted by the [NFT Contract] and transferred to the smart wallet address listed on the dashboard. Digging in a little more, you'll see some things that might be different than what you'd expect.
-
-#### Tokens Transferred and the NFT Contract
-
-In the `ERC-721 Tokens Transferred` section, click the link to `NFT Name (NFT)` to open up the overview page for the token. You'll see a list of transfers, with yours likely on the top. Click the address for the contract to open up the [view for the contract itself].
-
-You may be surprised to see that there are very few transactions listed for this contract, despite the list of transfers you can see on the token page, or the `Events` tab. Currently, Etherscan and BaseScan won't display transactions done via the paymaster.
-
-
-Blockchain explorers are service providers that provide information about the state of various blockchains. They are **not** a source of truth.
-
-It is possible for onchain activity to be missing from these services.
-
-
-
-#### The Bundler (Transaction Sender)
-
-Return to the transactions summary and look at the `From:` field. It will contain `0x1e6754b227c6ae4b0ca61d82f79d60660737554a`. What is this address? It's not your smart wallet address or signer address. If you mint another NFT from a different login, you'll get the same sender.
-
-This address is the [bundler], which is a special node that bundles user operations from the alt mempool into a single transaction and sends them to the single [EntryPoint] contract.
-
-#### EntryPoint
-
-The EntryPoint contract for Base Goerli is located at `0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789`. Strangely, in your transaction receipt you'll see that the transaction includes a transfer of ETH **from** the EntryPoint **to** the bundler. This transaction is how the bundler gets compensated for performing the service of bundling user ops and turning them into transactions -- the EntryPoint calculates the gas used by user ops and multiplies that by the fee percentage and send it to the bundler.
-
-### Review the Example Code
-
-Return to the [paymaster example] and review the readme. The section on _Copying into your code_ lists the three files you'll need to copy over to implement the paymaster in your own app. All three are extensively documented via comments. You'll also want to review how the demo app uses these to call a function.
-
-#### `SmartAccountContext.tsx`
-
-The first file, [`hooks/SmartAccountContext.tsx`] uses a React Context provider to create a ` SmartAccountProvider`` and pass it into your app. You can see it in use in `\_app.tsx`, with the regular `PrivyProvider` around it. Review the file in detail.
-
-Starting on line 63, the exported `SmartAccountProvider` does the following:
-
-1. Fetch the user's wallets and find their Privy wallet. This wallet is provided, and if need be created, by the `PrivyProvider`
-1. Set up state variables to manage and share connection status and the smart account itself
-1. Initialize an RPC client for the [Base Paymaster] (on Goerli). The URL is hardcoded in `lib/constants.ts`
-1. Initialize an ERC-4337 RPC client for Alchemy's network. This network is where the bundler address comes from
-1. Create a smart wallet. In this case, the `signer` is your EOA embedded wallet created by Privy and fetched in the first step
-1. The EOA address is displayed in the example app as `YOUR SIGNER ADDRESS`
-1. Initialize an Alchemy provider for the smart account signer, using Alchemy's [Account Kit].
-1. This creates the smart account and its address, which is displayed in the example app as `YOUR SMART WALLET ADDRESS`
-
-Finally, the `sendSponsoredUserOperation` function takes a traditional transaction, turns it into a user operation, adds the data for the paymaster to pay the gas, signs it, and sends it. Whew!
-
-If you want a deeper dive into the inner workings of this process, review the helper functions in [`user-operations.ts`].
-
-#### How to Call a Smart Contract Function with the Paymaster
-
-Open [`pages/dashboard.tsx`] and take a look at the `onMint` function on line 32. This function is used as the `oncClick` handler for the `Mint` button at the top of the dashboard.
-
-If you're used to working with wagmi, you'll find the process of sending and awaiting for confirmation of a transaction a little on the manual side. Most of this will be familiar if you've used viem directly, or have worked with _Ethers_.
-
-When a user clicks, the app first creates a viem `RpcTransactionRequest` for the `mint` function on the smart contract. The `smartContractAddress` is supplied by the `SmartAccountProvider`, and the `ABI` and contract `NFT_ADDRESS` are loaded from `lib/constants.ts`:
-
-```tsx
-{
- from: smartAccountAddress,
- to: NFT_ADDRESS,
- data: encodeFunctionData({
- abi: ABI,
- functionName: "mint",
- args: [smartAccountAddress],
- }),
-}
-```
-
-The app then updates the toast component to update the users while it `await`s first the `userOpHash`, then the `transactionHash`, indicating that transaction has completed successfully. It then updates the link in the toast to send the user to that transaction on Goerli BaseScan.
-
-### Implementing the Paymaster in your own App
-
-Create a new project using Privy's [`create-next-app`] template, and complete the setup instructions in the readme.
-
-Add an environment variable for `NEXT_PUBLIC_ALCHEMY_API_KEY` and paste in the an API key for a Base Goerli app. If you need a key, go to [add an app] and create a new one.
-
-#### Copying and Updating the Source Files
-
-Copy the `hooks` and `lib` folders into your _new_ project. You'll need to install some more dependencies. Use `npm` or `yarn` to add:
-
-- viem
-- react-dom
-- @alchemy/aa-accounts
-- @alchemy/aa-alchemy
-- @alchemy/aa-core
-
-Open `SmartAccountContext.tsx` in your project. You'll see an error for `getDefaultLightAccountFactory`. The name of this function has been updated to `getDefaultLightAccountFactoryAddress`. Change it in the import, and where it is used in the file in the call to `LightSmartContractAccount`.
-
-#### Updating to Use the User's Wallet
-
-The app is currently configured to find and use the user's embedded Privy wallet as the signer. To change this, modify the instantiation of the `SmartAccountProvider`. Instead of `find`ing the user's Privy wallet:
-
-```tsx
-// Old code to change
-
-// Get a list of all of the wallets (EOAs) the user has connected to your site
-const { wallets } = useWallets();
-// Find the embedded wallet by finding the entry in the list with a `walletClientType` of 'privy'
-const embeddedWallet = wallets.find((wallet) => wallet.walletClientType === 'privy');
-```
-
-Simply grab the first wallet in the list (you'll want to do something more elegant for a production app):
-
-```tsx
-// Updated Code
-
-// Get a list of all of the wallets (EOAs) the user has connected to your site
-const { wallets } = useWallets();
-
-// Grab the first wallet on the list
-// TODO: Implement the option to allow the user to choose another wallet
-const wallet = wallets[0];
-```
-
-Then, update the call at the bottom of `useEffect` to `createSmartWallet` if there is an `embeddedWallet` to instead create it if there is a `wallet`, using that `wallet`. You'll also need to update the dependency in the dependency array.
-
-```tsx
-useEffect(() => {
- // Other code
-
- if (wallet) createSmartWallet(wallet);
-}, [wallet?.address]);
-```
-
-#### Configuring the PrivyProvider and Adding SmartAccountProvider
-
-By default, the `PrivyProvider` allows logging in with a wallet or email address. To limit it to only the wallet, update the config. You can also set the default chain here. You'll need to import `baseGoerli` to do so.
-
-You also need to import and wrap the app with `SmartAccountProvider`, imported from `hooks/SmartAccountContext.tsx`.
-
-
-The `@alchemy/aa-core` package also exports `SmartAccountProvider` and this export takes precedence when VSCode attempts to help you by automatically adding the import. You'll know you've got the wrong one if `SmartAccountProvider` generates an error that:
-
-```text
-'SmartAccountProvider' cannot be used as a JSX component.
-Its instance type 'SmartAccountProvider' is not a valid JSX element.
-```
-
-
-
-```tsx
- router.push('/dashboard')}
- config={{
- loginMethods: ['wallet'],
- defaultChain: baseGoerli,
- }}
->
-
-
-
-
-```
-
-#### Checking Progress
-
-Grab the snippet from the original demo that displays the user's addresses, and add it to `dashboard.tsx` in the new project:
-
-```tsx
-
` for the `User Object` window.
-
-You'll need to import `BASE_GOERLI_SCAN_URL` from `constants.ts`. The `useSmartAccount` hook returns `smartAccountProvider` and `eoa`. Import it and add it under the `usePrivy` hook. You don't need them just yet, but go ahead and decompose `smartAccountProvider` and `sendSponsoredUserOperation` as well:
-
-```tsx
-const router = useRouter();
-const {
- ready,
- authenticated,
- user,
- logout,
- linkEmail,
- linkWallet,
- unlinkEmail,
- linkPhone,
- unlinkPhone,
- unlinkWallet,
- linkGoogle,
- unlinkGoogle,
- linkTwitter,
- unlinkTwitter,
- linkDiscord,
- unlinkDiscord,
-} = usePrivy();
-const { smartAccountAddress, smartAccountProvider, sendSponsoredUserOperation, eoa } =
- useSmartAccount();
-```
-
-Run the app. You'll now see your familiar wallet address as `YOUR SIGNER ADDRESS`!
-
-
-The app sometimes gets confused with login state after you've made changes to `config`. If you see the `Log In` button but clicking it does nothing, try manually navigating to `localhost:3000/dashboard` or clearing the cache.
-
-
-#### Calling a Smart Contract Function
-
-You've adjusted the foundation of the app to allow you to use the Base Goerli Paymaster with your normal wallet as the signer. Now, it's time to call a smart contract function.
-
-Start by using the `mint` function in the original example. In the `DashboardPage` component, add a state variable holding an empty element:
-
-```tsx
-const [transactionLink, setTransactionLink] = useState(<>>);
-```
-
-Then, add a variant of the original `onMint` function that sets this variable and has the code related to the toast removed.
-
-**Note:** make sure you change your wallet address in `args` to make sure the NFT is sent to your EOA wallet address!
-
-```tsx
-const onMint = async () => {
- // The mint button is disabled if either of these are undefined
- if (!smartAccountProvider || !smartAccountAddress) return;
-
- try {
- // From a viem `RpcTransactionRequest` (e.g. calling an ERC-721's `mint` method),
- // build and send a user operation. Gas fees will be sponsored by the Base Paymaster.
- const userOpHash = await sendSponsoredUserOperation({
- from: smartAccountAddress,
- to: NFT_ADDRESS,
- data: encodeFunctionData({
- abi: ABI,
- functionName: 'mint',
- args: [eoa?.address],
- }),
- });
-
- // Once we have a hash for the user operation, watch it until the transaction has
- // been confirmed.
- const transactionHash = await smartAccountProvider.waitForUserOperationTransaction(userOpHash);
-
- setTransactionLink(
-
- Successfully minted! Click here to see your transaction.
- ,
- );
- } catch (error) {
- setTransactionLink(
{'Mint failed with error: ' + error}
);
- }
-};
-```
-
-Finally, above where you added the addresses, add a button to call the function, and display the link to the transaction:
-
-```tsx
-;
-{
- transactionLink;
-}
-```
-
-Run it and confirm it works. You need the full transaction receipt for the process to finish, so expect to wait as long as 10 or 15 seconds.
-
-
-For simplicity, we've stripped out the code to disable the button while it is minting. You'll want to implement your own solution to avoid confusing your users!
-
-
-#### Calling Another Function
-
-The [Base Paymaster] on Goerli is very permissive. To call another function, all you need to do is to change the `RpcTransactionRequest` in `sendSponsoredUserOperation` to match the address, abi, function name, and arguments of your function on your smart contract.
-
-For example, to call the `claim` function in the Weighted Voting contract we've used in other tutorials, you'd simply need to import the Hardhat-style [artifact] for the contract and use it to call the function:
-
-```tsx
-const userOpHash = await sendSponsoredUserOperation({
- from: smartAccountAddress,
- to: weightedVoting.address as `0x${string}`,
- data: encodeFunctionData({
- abi: weightedVoting.abi,
- functionName: 'claim',
- }),
-});
-```
-
-
-The function in this example can only be called once per address. It will then fail, because one wallet cannot claim more than one batch of tokens.
-
-
-## Conclusion
-
-In this article, we've explored the transformative potential of Account Abstraction for the Ethereum ecosystem, highlighting how it enables smart contract accounts to initiate transactions without altering the core protocol. This innovation, coupled with the utilization of Privy for streamlined user onboarding and secure data management, marks a significant advancement towards reducing onboarding friction for onchain applications. Through a practical implementation involving Privy and the [Base Paymaster], we demonstrated how users can perform onchain actions without incurring gas fees, showcasing the adaptability and user-centric benefits of these technologies. This tutorial not only sheds light on the technical workings of Account Abstraction but also illustrates its practical application in enhancing the blockchain user experience.
-
-[ERC-4337]: https://eips.ethereum.org/EIPS/eip-4337
-[ethereum.org]: https://ethereum.org/en/developers/docs/accounts/#key-differences
-[Base Learn]: https://base.org/learn
-[Next.js]: https://nextjs.org/
-[Base Paymaster]: https://github.com/base-org/paymaster
-[Privy]: https://www.privy.io/
-[Alchemy's Account Kit]: https://www.alchemy.com/account-kit
-[Privy]: https://www.privy.io/
-[https://docs.privy.io/guide/frontend/embedded/overview]: https://docs.privy.io/guide/frontend/embedded/overview
-[Alchemy's Account Kit]: https://www.alchemy.com/account-kit
-[Privy's Quick Start]: https://docs.privy.io/guide/quickstart
-[https://github.com/privy-io/create-next-app]: https://github.com/privy-io/create-next-app
-[`PrivyClientConfig`]: https://docs.privy.io/reference/react-auth/modules#privyclientconfig
-[documented here]: https://docs.privy.io/reference/react-auth/interfaces/PrivyInterface
-[securely stored]: https://docs.privy.io/guide/security#user-data-management
-[paymaster example]: https://github.com/privy-io/base-paymaster-example/blob/main/README.md
-[ERC-4337]: https://eips.ethereum.org/EIPS/eip-4337
-[NFT Contract]: https://goerli.basescan.org/address/0x6527e5052de5521fe370ae5ec0afcc6cd5a221de
-[view for the contract itself]: https://goerli.basescan.org/address/0x6527e5052de5521fe370ae5ec0afcc6cd5a221de
-[Base Paymaster]: https://github.com/base-org/paymaster
-[EntryPoint]: https://github.com/eth-infinitism/account-abstraction/releases
-[bundler]: https://www.alchemy.com/overviews/what-is-a-bundler
-[`create-next-app`]: https://github.com/privy-io/create-next-app
-[add an app]: https://dashboard.alchemy.com/apps
-[Account Kit]: https://www.alchemy.com/blog/introducing-account-kit
-[`hooks/SmartAccountContext.tsx`]: https://github.com/privy-io/base-paymaster-example/blob/main/hooks/SmartAccountContext.tsx
-[`user-operations.ts`]: https://github.com/privy-io/base-paymaster-example/blob/main/lib/user-operations.ts
-[`pages/dashboard.tsx`]: https://github.com/privy-io/base-paymaster-example/blob/main/pages/dashboard.tsx
-[artifact]: https://gist.github.com/briandoyle81CB/2c2849b5723058792bece666f0a318cb
-
diff --git a/docs/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.mdx b/docs/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.mdx
deleted file mode 100644
index b3a34b273..000000000
--- a/docs/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.mdx
+++ /dev/null
@@ -1,367 +0,0 @@
-# Gasless Transactions on Base using Base Paymaster
-
-Still trying to onboard users to your app? Want to break free from the worries of gas transactions and sponsor them for your users on Base? Look no further!
-
-Base transaction fees are typically less than a penny, but the concept of gas can still be confusing for new users. You can abstract this away and improve your UX by using the **Base Paymaster**. The Paymaster allows you to:
-
-- Batch multi-step transactions
-- Create custom gasless experiences
-- Sponsor up to $10k monthly on mainnet (unlimited on testnet)
-
-> **Note:** If you need an increase in your sponsorship limit, please [reach out on Discord][Discord]!
-
-## Objectives
-
-1. Configure security measures to ensure safe and reliable transactions.
-2. Manage and allocate resources for sponsored transactions.
-3. Subsidize transaction fees for users, enhancing the user experience by making transactions free.
-4. Set up and manage sponsored transactions on various schedules, including weekly, monthly, and daily cadences.
-
-## Prerequisites
-
-This tutorial assumes you have:
-
-1. **A Coinbase Cloud Developer Platform Account**
- If not, sign up on the [CDP site]. Once you have your account, you can manage projects and utilize tools like the Paymaster.
-
-2. **Familiarity with Smart Accounts and ERC 4337**
- Smart Accounts are the backbone of advanced transaction patterns (e.g., bundling, sponsorship). If you’re new to ERC 4337, check out external resources like the official [EIP-4337 explainer](https://eips.ethereum.org/EIPS/eip-4337) before starting.
-
-3. **Foundry**
- Foundry is a development environment, testing framework, and smart contract toolkit for Ethereum. You’ll need it installed locally for generating key pairs and interacting with smart contracts.
-
-> **Testnet vs. Mainnet**
-> If you prefer not to spend real funds, you can switch to **Base Goerli** (testnet). The steps below are conceptually the same. Just select _Base Goerli_ in the Coinbase Developer Platform instead of _Base Mainnet_, and use a contract deployed on Base testnet for your allowlisted methods.
-
-## Set Up a Base Paymaster & Bundler
-
-In this section, you will configure a Paymaster to sponsor payments on behalf of a specific smart contract for a specified amount.
-
-1. **Navigate to the [Coinbase Developer Platform].**
-2. Create or select your project from the upper left corner of the screen.
-3. Click on the **Paymaster** tool from the left navigation.
-4. Go to the **Configuration** tab and copy the **RPC URL** to your clipboard — you’ll need this shortly in your code.
-
-### Screenshots
-
-- **Selecting your project**
-
- 
-
-
-- **Navigating to the Paymaster tool**
-
- 
-
-
-- **Configuration screen**
-
- 
-
-
-### Allowlist a Sponsorable Contract
-
-1. From the Configuration page, ensure **Base Mainnet** (or **Base Goerli** if you’re testing) is selected.
-2. Enable your paymaster by clicking the toggle button.
-3. Click **Add** to add an allowlisted contract.
-4. For this example, add [`0x83bd615eb93eE1336acA53e185b03B54fF4A17e8`][simple NFT contract], and add the function `mintTo(address)`.
-
-
-
-
-
-> **Use your own contract**
-> We use a [simple NFT contract][simple NFT contract] on Base mainnet as an example. Feel free to substitute your own.
-
-### Global & Per User Limits
-
-Scroll down to the **Per User Limit** section. You can set:
-
-- **Dollar amount limit** or **number of UserOperations** per user
-- **Limit cycles** that reset daily, weekly, or monthly
-
-For example, you might set:
-
-- `max USD` to `$0.05`
-- `max UserOperation` to `1`
-
-This means **each user** can only have \$0.05 in sponsored gas and **1** user operation before the cycle resets.
-
-> **Limit Cycles**
-> These reset based on the selected cadence (daily, weekly, monthly).
-
-Next, **Set the Global Limit**. For example, set this to `$0.07` so that once the entire paymaster has sponsored \$0.07 worth of gas (across all users), no more sponsorship occurs unless you raise the limit.
-
-
-
-
-
-## Test Your Paymaster Policy
-
-Now let’s verify that these policies work. We’ll:
-
-1. Create two local key pairs (or use private keys you own).
-2. Generate two Smart Accounts.
-3. Attempt to sponsor multiple transactions to see your policy in action.
-
-### Installing Foundry
-
-1. Ensure you have **Rust** installed. If not:
- ```bash Terminal
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- ```
-2. Install Foundry:
- ```bash Terminal
- curl -L https://foundry.paradigm.xyz | bash
- foundryup
- ```
-3. Verify it works:
- ```bash Terminal
- cast --help
- ```
- If you see Foundry usage info, you’re good to go!
-
-### Create Your Project & Generate Key Pairs
-
-1. Make a new folder and install dependencies:
- ```bash Terminal
- mkdir sponsored_transactions
- cd sponsored_transactions
- npm init es6
- npm install permissionless
- npm install viem
- touch index.js
- ```
-2. Generate two key pairs with Foundry:
- ```bash Terminal
- cast wallet new
- cast wallet new
- ```
- You’ll see something like:
- ```bash Terminal
- Successfully created new keypair.
- Address: 0xD440D746...
- Private key: 0x01c9720c1dfa3c9...
- ```
- **Store these private keys somewhere safe** — ideally in a `.env` file.
-
-### Project Structure With Environment Variables
-
-Create a `.env` file in `sponsored_transactions`:
-
-```bash Terminal
-PAYMASTER_RPC_URL=https://api.developer.coinbase.com/rpc/v1/base/
-PRIVATE_KEY_1=0x01c9720c1dfa3c9...
-PRIVATE_KEY_2=0xbcd6fbc1dfa3c9...
-```
-
-> **Security**
-> **Never** commit `.env` files to a public repo!
-
-## Example `index.js` (Using Twoslash)
-
-Below is a full example of how you might structure `index.js`.
-We’ll use **twoslash** code blocks (````js twoslash`) to highlight key lines and explanations.
-
-```js
-// --- index.js ---
-// @noErrors
-
-// 1. Import modules and environment variables
-import 'dotenv/config';
-import { http, createPublicClient, encodeFunctionData } from 'viem';
-import { base } from 'viem/chains';
-import { createSmartAccountClient } from 'permissionless';
-import { privateKeyToSimpleSmartAccount } from 'permissionless/accounts';
-import { createPimlicoPaymasterClient } from 'permissionless/clients/pimlico';
-
-// 2. Retrieve secrets from .env
-// Highlight: environment variables for paymaster, private keys
-const rpcUrl = process.env.PAYMASTER_RPC_URL; // highlight
-const firstPrivateKey = process.env.PRIVATE_KEY_1; // highlight
-const secondPrivateKey = process.env.PRIVATE_KEY_2; // highlight
-
-// 3. Declare Base addresses (entrypoint & factory)
-const baseEntryPoint = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789';
-const baseFactoryAddress = '0x15Ba39375ee2Ab563E8873C8390be6f2E2F50232';
-
-// 4. Create a public client for Base
-const publicClient = createPublicClient({
- chain: base,
- transport: http(rpcUrl),
-});
-
-// 5. Setup Paymaster client
-const cloudPaymaster = createPimlicoPaymasterClient({
- chain: base,
- transport: http(rpcUrl),
- entryPoint: baseEntryPoint,
-});
-
-// 6. Create Smart Accounts from the private keys
-async function initSmartAccounts() {
- const simpleAccount = await privateKeyToSimpleSmartAccount(publicClient, {
- privateKey: firstPrivateKey,
- factoryAddress: baseFactoryAddress,
- entryPoint: baseEntryPoint,
- });
-
- const simpleAccount2 = await privateKeyToSimpleSmartAccount(publicClient, {
- privateKey: secondPrivateKey,
- factoryAddress: baseFactoryAddress,
- entryPoint: baseEntryPoint,
- });
-
- // 7. Create SmartAccountClient for each
- const smartAccountClient = createSmartAccountClient({
- account: simpleAccount,
- chain: base,
- bundlerTransport: http(rpcUrl),
- middleware: {
- sponsorUserOperation: cloudPaymaster.sponsorUserOperation,
- },
- });
-
- const smartAccountClient2 = createSmartAccountClient({
- account: simpleAccount2,
- chain: base,
- bundlerTransport: http(rpcUrl),
- middleware: {
- sponsorUserOperation: cloudPaymaster.sponsorUserOperation,
- },
- });
-
- return { smartAccountClient, smartAccountClient2 };
-}
-
-// 8. ABI for the NFT contract
-const nftAbi = [
- // ...
- // truncated for brevity
-];
-
-// 9. Example function to send a transaction from a given SmartAccountClient
-async function sendTransaction(client, recipientAddress) {
- try {
- // encode the "mintTo" function call
- const callData = encodeFunctionData({
- abi: nftAbi,
- functionName: 'mintTo',
- args: [recipientAddress], // highlight: specify who gets the minted NFT
- });
-
- const txHash = await client.sendTransaction({
- account: client.account,
- to: '0x83bd615eb93eE1336acA53e185b03B54fF4A17e8', // address of the NFT contract
- data: callData,
- value: 0n,
- });
-
- console.log(`✅ Transaction successfully sponsored for ${client.account.address}`);
- console.log(`🔍 View on BaseScan: https://basescan.org/tx/${txHash}`);
- } catch (error) {
- console.error('Transaction failed:', error);
- }
-}
-
-// 10. Main flow: init accounts, send transactions
-(async () => {
- const { smartAccountClient, smartAccountClient2 } = await initSmartAccounts();
-
- // Send a transaction from the first account
- await sendTransaction(smartAccountClient, smartAccountClient.account.address);
-
- // Send a transaction from the second account
- // For variety, let’s also mint to the second account's own address
- await sendTransaction(smartAccountClient2, smartAccountClient2.account.address);
-})();
-```
-
-> **Note**:
->
-> - Run this via `node index.js` from your project root.
-> - If your Paymaster settings are strict (e.g., limit 1 transaction per user), the second time you run the script, you may get a “request denied” error, indicating the policy is working.
-
-## Hitting Policy Limits & Troubleshooting
-
-1. **Per-User Limit**
- If you see an error like:
-
- ```json
- {
- "code": -32001,
- "message": "request denied - rejected due to maximum per address transaction count reached"
- }
- ```
-
- That means you’ve hit your **UserOperation** limit for a single account. Return to the [Coinbase Developer Platform] UI to adjust the policy.
-
-2. **Global Limit**
- If you repeatedly run transactions and eventually see:
- ```json
- {
- "code": -32001,
- "message": "request denied - rejected due to max global usd Spend Permission reached"
- }
- ```
- You’ve hit the **global** limit of sponsored gas. Increase it in the CDP dashboard and wait a few minutes for changes to take effect.
-
-## Verifying Token Ownership (Optional)
-
-Want to confirm the token actually minted? You can read the NFT’s `balanceOf` function:
-
-```js
-import { readContract } from 'viem'; // highlight
-
-// example function
-async function checkNftBalance(publicClient, contractAddress, abi, ownerAddress) {
- const balance = await publicClient.readContract({
- address: contractAddress,
- abi,
- functionName: 'balanceOf',
- args: [ownerAddress],
- });
- console.log(`NFT balance of ${ownerAddress} is now: ${balance}`);
-}
-```
-
-## Conclusion
-
-In this tutorial, you:
-
-- Set up and **configured** a Base Paymaster on the Coinbase Developer Platform.
-- **Allowlisted** a contract and specific function (`mintTo`) for sponsorship.
-- Established **per-user** and **global** sponsorship **limits** to control costs.
-- Demonstrated the **sponsorship flow** with Smart Accounts using `permissionless`, `viem`, and Foundry-generated private keys.
-
-This approach can greatly improve your dApp’s user experience by removing gas friction. For more complex sponsorship schemes (like daily or weekly cycles), simply tweak your per-user and global limit settings in the Coinbase Developer Platform.
-
-> **Next Steps**
->
-> - Use a [proxy service][proxy service] for better endpoint security.
-> - Deploy your own contracts and allowlist them.
-> - Experiment with bundling multiple calls into a single sponsored transaction.
-
-## References
-
-- [list of factory addresses]
-- [Discord]
-- [CDP site]
-- [Coinbase Developer Platform]
-- [UI]
-- [proxy service]
-- [Paymaster Tool]
-- [Foundry Book installation guide]
-- [simple NFT contract]
-
-[list of factory addresses]: https://docs.alchemy.com/reference/factory-addresses
-[Discord]: https://discord.com/invite/cdp
-[CDP site]: https://portal.cdp.coinbase.com/
-[Coinbase Developer Platform]: https://portal.cdp.coinbase.com/
-[UI]: https://portal.cdp.coinbase.com/products/bundler-and-paymaster
-[proxy service]: https://www.smartwallet.dev/guides/paymasters
-[Paymaster Tool]: https://portal.cdp.coinbase.com/products/bundler-and-paymaster
-[Foundry Book installation guide]: https://book.getfoundry.sh/getting-started/installation
-[simple NFT contract]: https://basescan.org/token/0x83bd615eb93ee1336aca53e185b03b54ff4a17e8
-
-**Happy Building on Base!**
diff --git a/docs/learn/onchain-app-development/cross-chain/bridge-tokens-with-layerzero.mdx b/docs/learn/onchain-app-development/cross-chain/bridge-tokens-with-layerzero.mdx
deleted file mode 100644
index 3fefab957..000000000
--- a/docs/learn/onchain-app-development/cross-chain/bridge-tokens-with-layerzero.mdx
+++ /dev/null
@@ -1,597 +0,0 @@
----
-title: Sending messages from Base to other chains using LayerZero V2
-description: A tutorial that teaches how to use LayerZero V2 to perform cross-chain messaging from Base Sepolia testnet to Optimism Sepolia testnet.
-authors:
- - taycaldwell
----
-
-# Sending messages from Base to other chains using LayerZero V2
-
-This tutorial will guide you through the process of sending cross-chain message data from a Base smart contract to another smart contract on a different chain using LayerZero V2.
-
-## Objectives
-
-By the end of this tutorial you should be able to do the following:
-
-- Set up a smart contract project for Base using Foundry
-- Install the LayerZero smart contracts as a dependency
-- Use LayerZero to send messages and from smart contracts on Base to smart contracts on different chains
-- Deploy and test your smart contracts on Base testnet
-
-## Prerequisites
-
-### Foundry
-
-This tutorial requires you to have Foundry installed.
-
-- From the command-line (terminal), run: `curl -L https://foundry.paradigm.xyz | bash`
-- Then run `foundryup`, to install the latest (nightly) build of Foundry
-
-For more information, see the Foundry Book [installation guide](https://book.getfoundry.sh/getting-started/installation).
-
-### Coinbase Wallet
-
-In order to deploy a smart contract, you will first need a wallet. You can create a wallet by downloading the Coinbase Wallet browser extension.
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-To complete this tutorial, you will need to fund a wallet with ETH on Base Sepolia and Optimism Sepolia.
-
-The ETH is required for covering gas fees associated with deploying smart contracts to each network.
-
-- To fund your wallet with ETH on Base Sepolia, visit a faucet listed on the [Base Faucets](/base-chain/tools/network-faucets) page.
-- To fund your wallet with ETH on Optimism Sepolia, visit a faucet listed on the [Optimism Faucets](https://docs.optimism.io/builders/tools/faucets) page.
-
-## What is LayerZero?
-
-LayerZero is an interoperability protocol that allows developers to build applications (and tokens) that can connect to multiple blockchains. LayerZero defines these types of applications as "omnichain" applications.
-
-The LayerZero protocol is made up of immutable on-chain [Endpoints](https://docs.layerzero.network/v2/concepts/protocol/layerzero-endpoint), a configurable [Security Stack](https://docs.layerzero.network/explore/decentralized-verifier-networks), and a permissionless set of [Executors](https://docs.layerzero.network/v2/concepts/permissionless-execution/executors) that transfer messages between chains.
-
-### High-level concepts
-
-#### Endpoints
-
-Endpoints are immutable LayerZero smart contracts that implement a standardized interface for your own smart contracts to use and in order to manage security configurations and send and receive messages.
-
-#### Security Stack (DVNs)
-
-The [Security Stack](https://docs.layerzero.network/explore/decentralized-verifier-networks) is a configurable set of required and optional Decentralized Verifier Networks (DVNs). The DVNs are used to verify message payloads to ensure integrity of your application's messages.
-
-#### Executors
-
-[Executors](https://docs.layerzero.network/v2/concepts/permissionless-execution/executors) are responsible for initiating message delivery. They will automatically execute the `lzReceive` function of the endpoint on the destination chain once a message has been verified by the Security Stack.
-
-## Creating a project
-
-Before you begin, you need to set up your smart contract development environment by creating a Foundry project.
-
-To create a new Foundry project, first create a new directory:
-
-```bash
-mkdir myproject
-```
-
-Then run:
-
-```bash
-cd myproject
-forge init
-```
-
-This will create a Foundry project with the following basic layout:
-
-```bash
-.
-├── foundry.toml
-├── script
-├── src
-└── test
-```
-
-
-You can delete the `src/Counter.sol`, `test/Counter.t.sol`, and `script/Counter.s.sol` boilerplate files that were generated with the project, as you will not be needing them.
-
-
-## Installing the LayerZero smart contracts
-
-To use LayerZero within your Foundry project, you need to install the LayerZero smart contracts and their dependencies using `forge install`.
-
-To install LayerZero smart contracts and their dependencies, run the following commands:
-
-```bash
-forge install GNSPS/solidity-bytes-utils --no-commit
-forge install OpenZeppelin/openzeppelin-contracts@v4.9.4 --no-commit
-forge install LayerZero-Labs/LayerZero-v2 --no-commit
-```
-
-Once installed, update your `foundry.toml` file by appending the following lines:
-
-```bash
-remappings = [
- '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts',
- 'solidity-bytes-utils/=lib/solidity-bytes-utils',
- '@layerzerolabs/lz-evm-oapp-v2/=lib/LayerZero-v2/oapp',
- '@layerzerolabs/lz-evm-protocol-v2/=lib/LayerZero-v2/protocol',
- '@layerzerolabs/lz-evm-messagelib-v2/=lib/LayerZero-v2/messagelib',
-]
-
-```
-
-## Getting started with LayerZero
-
-LayerZero provides a smart contract standard called [OApp](https://docs.layerzero.network/v2/developers/evm/oapp/overview) that is intended for omnichain messaging and configuration.
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import { OAppSender } from "./OAppSender.sol";
-import { OAppReceiver, Origin } from "./OAppReceiver.sol";
-import { OAppCore } from "./OAppCore.sol";
-
-abstract contract OApp is OAppSender, OAppReceiver {
- constructor(address _endpoint) OAppCore(_endpoint, msg.sender) {}
-
- function oAppVersion() public pure virtual returns (uint64 senderVersion, uint64 receiverVersion) {
- senderVersion = SENDER_VERSION;
- receiverVersion = RECEIVER_VERSION;
- }
-}
-```
-
-
-You can view the source code for this contract on [GitHub](https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/oapp/contracts/oapp/OApp.sol).
-
-
-To get started using LayerZero, developers simply need to inherit from the [OApp](https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/oapp/contracts/oapp/OApp.sol) contract, and implement the following two inherited functions:
-
-- `_lzSend`: A function used to send an omnichain message
-- `_lzReceive`: A function used to receive an omnichain message
-
-In this tutorial, you will be implementing the [OApp](https://docs.layerzero.network/v2/developers/evm/oapp/overview) standard into your own project to add the capability to send messages from a smart contract on Base to a smart contract on Optimism.
-
-
-An extension of the [OApp](https://docs.layerzero.network/v2/developers/evm/oapp/overview) contract standard known as [OFT](https://docs.layerzero.network/contracts/oft) is also available for supporting omnichain fungible token transfers.
-
-
-
-For more information on transferring tokens across chains using LayerZero, visit the [LayerZero documentation](https://docs.layerzero.network/contracts/oft).
-
-
-## Writing the smart contract
-
-To get started, create a new Solidity smart contract file in your project's `src/` directory named `ExampleContract.sol`, and add the following content:
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import { OApp, Origin, MessagingFee } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
-
-contract ExampleContract is OApp {
- constructor(address _endpoint, address _owner) OApp(_endpoint, _owner) {}
-}
-```
-
-The code snippet above defines a new smart contract named `ExampleContract` that extends the `OApp` contract standard.
-
-The contract's constructor expects two arguments:
-
-- `_endpoint`: The [LayerZero Endpoint](https://docs.layerzero.network/v2/deployments/deployed-contracts) `address` for the chain the smart contract is deployed to.
-- `_owner`: The `address` of the owner of the smart contract.
-
-
-[LayerZero Endpoints](https://docs.layerzero.network/v2/deployments/deployed-contracts) are smart contracts that expose an interface for OApp contracts to manage security configurations and send and receive messages via the LayerZero protocol.
-
-
-### Implementing message sending (`_lzSend`)
-
-To send messages to another chain, your smart contract must call the `_lzSend` function inherited from the [OApp](https://docs.layerzero.network/v2/developers/evm/oapp/overview) contract.
-
-Add a new custom function named `sendMessage` to your smart contract that has the following content:
-
-```solidity
-/// @notice Sends a message from the source chain to the destination chain.
-/// @param _dstEid The endpoint ID of the destination chain.
-/// @param _message The message to be sent.
-/// @param _options The message execution options (e.g. gas to use on destination).
-function sendMessage(uint32 _dstEid, string memory _message, bytes calldata _options) external payable {
- bytes memory _payload = abi.encode(_message); // Encode the message as bytes
- _lzSend(
- _dstEid,
- _payload,
- _options,
- MessagingFee(msg.value, 0), // Fee for the message (nativeFee, lzTokenFee)
- payable(msg.sender) // The refund address in case the send call reverts
- );
-}
-```
-
-The `sendMessage` function above calls the inherited `_lzSend` function, while passing in the following expected data:
-
-| Name | Type | Description |
-| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `_dstEid` | `uint32` | The [endpoint ID](https://docs.layerzero.network/v2/deployments/deployed-contracts?stages=mainnet&chains=base) of the destination chain to send the message to. |
-| `_payload` | `bytes` | The message (encoded) to send. |
-| `_options` | `bytes` | [Additional options](https://docs.layerzero.network/v2/concepts/message-options#why-do-options-matter) when sending the message, such as how much gas should be used when receiving the message. |
-| `_fee` | [`MessagingFee`](https://github.com/LayerZero-Labs/LayerZero-v2/blob/c3213200dfe8fabbf7d92c685590d34e6e70da43/protocol/contracts/interfaces/ILayerZeroEndpointV2.sol#L24) | The calculated fee for sending the message. |
-| `_refundAddress` | `address` | The `address` that will receive any excess fee values sent to the endpoint in case the `_lzSend` execution reverts. |
-
-### Implementing gas fee estimation (`_quote`)
-
-As shown in the table provided in the last section, the `_lzSend` function expects an estimated gas [fee](https://github.com/LayerZero-Labs/LayerZero-v2/blob/c3213200dfe8fabbf7d92c685590d34e6e70da43/protocol/contracts/interfaces/ILayerZeroEndpointV2.sol#L24) to be provided when sending a message (`_fee`).
-
-Therefore, sending a message using the `sendMessage` function of your contract, you first need to estimate the associated gas fees.
-
-There are multiple fees incurred when sending a message across chains using LayerZero, including: paying for gas on the source chain, fees paid to DVNs validating the message, and gas on the destination chain. Luckily, LayerZero bundles all of these fees together into a single fee to be paid by the `msg.sender`, and LayerZero Endpoints expose a `_quote` function to estimate this fee.
-
-Add a new function to your `ExampleContract` contract called `estimateFee` that calls the `_quote` function, as shown below:
-
-```solidity
-/// @notice Estimates the gas associated with sending a message.
-/// @param _dstEid The endpoint ID of the destination chain.
-/// @param _message The message to be sent.
-/// @param _options The message execution options (e.g. gas to use on destination).
-/// @return nativeFee Estimated gas fee in native gas.
-/// @return lzTokenFee Estimated gas fee in ZRO token.
-function estimateFee(
- uint32 _dstEid,
- string memory _message,
- bytes calldata _options
-) public view returns (uint256 nativeFee, uint256 lzTokenFee) {
- bytes memory _payload = abi.encode(_message);
- MessagingFee memory fee = _quote(_dstEid, _payload, _options, false);
- return (fee.nativeFee, fee.lzTokenFee);
-}
-```
-
-The `estimateFee` function above calls the inherited `_quote` function, while passing in the following expected data:
-
-| Name | Type | Description |
-| :-------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `_dstEid` | `uint32` | The [endpoint ID](https://docs.layerzero.network/v2/deployments/deployed-contracts?stages=mainnet&chains=base) of the destination chain the message will be sent to. |
-| `_payload` | `bytes` | The message (encoded) that will be sent. |
-| `_options` | `bytes` | [Additional options](https://docs.layerzero.network/v2/concepts/message-options#why-do-options-matter) when sending the message, such as how much gas should be used when receiving the message. |
-| `_payInLzToken` | `bool` | Boolean flag for which token to use when returning the fee (native or ZRO token). |
-
-
-Your contract's `estimateFee` function should always be called immediately before calling `sendMessage` to accurately estimate associated gas fees.
-
-
-### Implementing message receiving (`_lzReceive`)
-
-To receive messages on the destination chain, your smart contract must override the `_lzReceive` function inherited from the [OApp](https://docs.layerzero.network/v2/developers/evm/oapp/overview) contract.
-
-Add the following code snippet to your `ExampleContract` contract to override the `_lzReceive` function:
-
-```solidity
-/// @notice Entry point for receiving messages.
-/// @param _origin The origin information containing the source endpoint and sender address.
-/// - srcEid: The source chain endpoint ID.
-/// - sender: The sender address on the src chain.
-/// - nonce: The nonce of the message.
-/// @param _guid The unique identifier for the received LayerZero message.
-/// @param _message The payload of the received message.
-/// @param _executor The address of the executor for the received message.
-/// @param _extraData Additional arbitrary data provided by the corresponding executor.
-function _lzReceive(
- Origin calldata _origin,
- bytes32 _guid,
- bytes calldata payload,
- address _executor,
- bytes calldata _extraData
- ) internal override {
- data = abi.decode(payload, (string));
- // other logic
-}
-```
-
-The overridden `_lzReceive` function receives the following arguments when receiving a message:
-
-| Name | Type | Description |
-| :------------ | :-------- | :-------------------------------------------------------------------------------------------------------------------- |
-| `_origin` | `Origin` | The origin information containing the source endpoint and sender address. |
-| `_guid` | `bytes32` | The unique identifier for the received LayerZero message. |
-| `payload` | `bytes` | The payload of the received message (encoded). |
-| `_executor` | `address` | The `address` of the [Executor](https://docs.layerzero.network/v2/concepts/permissionless-execution/executors) for the received message. |
-| `_extraData ` | `bytes` | Additional arbitrary data provided by the corresponding [Executor](https://docs.layerzero.network/v2/concepts/permissionless-execution/executors). |
-
-Note that the overridden method decodes the message payload, and stores the string into a variable named `data` that you can read from later to fetch the latest message.
-
-Add the `data` field as a member variable to your contract:
-
-```solidity
-contract ExampleContract is OApp {
-
- // highlight-next-line
- string public data;
-
- constructor(address _endpoint) OApp(_endpoint, msg.sender) {}
-}
-```
-
-
-Overriding the `_lzReceive` function allows you to provide any custom logic you wish when receiving messages, including making a call back to the source chain by invoking `_lzSend`. Visit the LayerZero [Message Design Patterns](https://docs.layerzero.network/v2/developers/evm/oapp/message-design-patterns) for common messaging flows.
-
-
-### Final code
-
-Once you complete all of the steps above, your contract should look like this:
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import { OApp, Origin, MessagingFee } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
-
-contract ExampleContract is OApp {
-
- string public data;
-
- constructor(address _endpoint) OApp(_endpoint, msg.sender) {}
-
- /// @notice Sends a message from the source chain to the destination chain.
- /// @param _dstEid The endpoint ID of the destination chain.
- /// @param _message The message to be sent.
- /// @param _options The message execution options (e.g. gas to use on destination).
- function sendMessage(uint32 _dstEid, string memory _message, bytes calldata _options) external payable {
- bytes memory _payload = abi.encode(_message); // Encode the message as bytes
- _lzSend(
- _dstEid,
- _payload,
- _options,
- MessagingFee(msg.value, 0), // Fee for the message (nativeFee, lzTokenFee)
- payable(msg.sender) // The refund address in case the send call reverts
- );
- }
-
- /// @notice Estimates the gas associated with sending a message.
- /// @param _dstEid The endpoint ID of the destination chain.
- /// @param _message The message to be sent.
- /// @param _options The message execution options (e.g. gas to use on destination).
- /// @return nativeFee Estimated gas fee in native gas.
- /// @return lzTokenFee Estimated gas fee in ZRO token.
- function estimateFee(
- uint32 _dstEid,
- string memory _message,
- bytes calldata _options
- ) public view returns (uint256 nativeFee, uint256 lzTokenFee) {
- bytes memory _payload = abi.encode(_message);
- MessagingFee memory fee = _quote(_dstEid, _payload, _options, false);
- return (fee.nativeFee, fee.lzTokenFee);
- }
-
- /// @notice Entry point for receiving messages.
- /// @param _origin The origin information containing the source endpoint and sender address.
- /// - srcEid: The source chain endpoint ID.
- /// - sender: The sender address on the src chain.
- /// - nonce: The nonce of the message.
- /// @param _guid The unique identifier for the received LayerZero message.
- /// @param _message The payload of the received message.
- /// @param _executor The address of the executor for the received message.
- /// @param _extraData Additional arbitrary data provided by the corresponding executor.
- function _lzReceive(
- Origin calldata _origin,
- bytes32 _guid,
- bytes calldata payload,
- address _executor,
- bytes calldata _extraData
- ) internal override {
- data = abi.decode(payload, (string));
- }
-}
-```
-
-## Compiling the smart contract
-
-Compile the smart contract to ensure it builds without any errors.
-
-To compile your smart contract, run:
-
-```bash
-forge build
-```
-
-## Deploying the smart contract
-
-### Setting up your wallet as the deployer
-
-Before you can deploy your smart contract to various chains you will need to set up a wallet to be used as the deployer.
-
-To do so, you can use the [`cast wallet import`](https://getfoundry.sh/cast/reference/wallet/import#cast-wallet-import) command to import the private key of the wallet into Foundry's securely encrypted keystore:
-
-```bash
-cast wallet import deployer --interactive
-```
-
-After running the command above, you will be prompted to enter your private key, as well as a password for signing transactions.
-
-
-For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key). **It is critical that you do NOT commit this to a public repo**.
-
-
-To confirm that the wallet was imported as the `deployer` account in your Foundry project, run:
-
-```bash
-cast wallet list
-```
-
-### Setting up environment variables
-
-To setup your environment, create an `.env` file in the home directory of your project, and add the RPC URLs and [LayerZero Endpoint](https://docs.layerzero.network/v2/deployments/deployed-contracts?chains=base-sepolia%2Coptimism-sepolia) information for both Base Sepolia and Optimism Sepolia testnets:
-
-```bash
-BASE_SEPOLIA_RPC="https://sepolia.base.org"
-BASE_SEPOLIA_LZ_ENDPOINT=0x6EDCE65403992e310A62460808c4b910D972f10f
-BASE_SEPOLIA_LZ_ENDPOINT_ID=40245
-
-OPTIMISM_SEPOLIA_RPC="https://sepolia.optimism.io"
-OPTIMISM_SEPOLIA_LZ_ENDPOINT=0x6EDCE65403992e310A62460808c4b910D972f10f
-OPTIMISM_SEPOLIA_LZ_ENDPOINT_ID=40232
-```
-
-Once the `.env` file has been created, run the following command to load the environment variables in the current command line session:
-
-```
-source .env
-```
-
-With your contract compiled and environment setup, you are now ready to deploy the smart contract to different networks.
-
-### Deploying the smart contract to Base Sepolia
-
-To deploy a smart contract using Foundry, you can use the `forge create` command. The command requires you to specify the smart contract you want to deploy, an RPC URL of the network you want to deploy to, and the account you want to deploy with.
-
-
-Your wallet must be funded with ETH on the Base Sepolia and Optimism Sepolia to cover the gas fees associated with the smart contract deployment. Otherwise, the deployment will fail.
-
-To get testnet ETH, see the [prerequisites](#prerequisites).
-
-
-To deploy the `ExampleContract` smart contract to the Base Sepolia testnet, run the following command:
-
-```bash
-forge create ./src/ExampleContract.sol:ExampleContract --rpc-url $BASE_SEPOLIA_RPC --constructor-args $BASE_SEPOLIA_LZ_ENDPOINT --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-After running the command above, the contract will be deployed on the Base Sepolia test network. You can view the deployment status and contract by using a [block explorer](/base-chain/tools/block-explorers).
-
-### Deploying the smart contract to Optimism Sepolia
-
-To deploy the `ExampleContract` smart contract to the Optimism Sepolia testnet, run the following command:
-
-```bash
-forge create ./src/ExampleContract.sol:ExampleContract --rpc-url $OPTIMISM_SEPOLIA_RPC --constructor-args $OPTIMISM_SEPOLIA_LZ_ENDPOINT --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-After running the command above, the contract will be deployed on the Optimism Sepolia test network. You can view the deployment status and contract by using the [OP Sepolia block explorer](https://sepolia-optimism.etherscan.io/).
-
-## Opening the messaging channels
-
-Once your contract has been deployed to Base Sepolia and Optimism Sepolia, you will need to open the messaging channels between the two contracts so that they can send and receive messages from one another. This is done by calling the `setPeer` function on the contract.
-
-The `setPeer` function expects the following arguments:
-
-| Name | Type | Description |
-| :------ | :-------- | :------------------------------------------------------------------------------------------------------- |
-| `_eid` | `uint32` | The [endpoint ID](https://docs.layerzero.network/v2/deployments/deployed-contracts) of the destination chain. |
-| `_peer` | `bytes32` | The contract address of the OApp contract on the destination chain. |
-
-### Setting the peers
-
-Foundry provides the `cast` command-line tool that can be used to interact with deployed smart contracts and call their functions.
-
-To set the peer of your `ExampleContract` contracts, you can use `cast` to call the `setPeer` function while providing the [endpoint ID](https://docs.layerzero.network/v2/deployments/deployed-contracts) and address (in bytes) of the deployed contract on the respective destination chain.
-
-To set the peer of the Base Sepolia contract to the Optimism Sepolia contract, run the following command:
-
-```bash
-cast send --rpc-url $BASE_SEPOLIA_RPC "setPeer(uint32, bytes32)" $OPTIMISM_SEPOLIA_LZ_ENDPOINT_ID --account deployer
-```
-
-
-Replace `` with the contract address of your deployed `ExampleContract` contract on Base Sepolia, and`` with the contract address (as bytes) of your deployed `ExampleContract` contract on Optimism Sepolia before running the provided `cast` command.
-
-
-To set the peer of the Optimism Sepolia contract to the Base Sepolia contract, run the following command:
-
-```bash
-cast send --rpc-url $OPTIMISM_SEPOLIA_RPC "setPeer(uint32, bytes32)" $BASE_SEPOLIA_LZ_ENDPOINT_ID --account deployer
-```
-
-
-Replace `` with the contract address of your deployed `ExampleContract` contract on Optimism Sepolia, and`` with the contract address (as bytes) of your deployed `ExampleContract` contract on Base Sepolia before running the provided `cast` command.
-
-
-## Sending messages
-
-Once peers have been set on each contract, they are now able to send and receive messages from one another.
-
-Sending a message using the newly created `ExampleContract` contract can be done in three steps:
-
-1. Build [message options](https://docs.layerzero.network/v2/developers/evm/toolbox#building-message-options) to specify logic associated with the message transaction
-2. Call the `estimateFee` function to estimate the gas fee for sending a message
-3. Call the `sendMessage` function to send a message
-
-### Building message options
-
-The `estimateFee` and `sendMessage` custom functions of the `ExampleContract` contract both require a [message options](https://docs.layerzero.network/v2/developers/evm/toolbox#building-message-options) (`_options`) argument to be provided.
-
-Message options allow you to specify arbitrary logic as part of the message transaction, such as the gas amount the [Executor](https://docs.layerzero.network/v2/concepts/permissionless-execution/executors) pays for message delivery, the order of message execution, or dropping an amount of gas to a destination address.
-
-LayerZero provides a [Solidity](https://github.com/LayerZero-Labs/LayerZero-v2/blob/ccfd0d38f83ca8103b14ab9ca77f32e0419510ff/oapp/contracts/oapp/libs/OptionsBuilder.sol#L12) library and [TypeScript SDK](https://docs.layerzero.network/plugins#layerzerolabssg-sdk) for building these message options.
-
-As an example, below is a Foundry script that uses OptionsBuilder from the Solidity library to generate message options (as `bytes`) that set the gas amount that the Executor will pay upon message delivery to `200000` wei:
-
-```solidity
-pragma solidity ^0.8.0;
-
-import {Script, console2} from "forge-std/Script.sol";
-import { OptionsBuilder } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OptionsBuilder.sol";
-
-contract OptionsScript is Script {
- using OptionsBuilder for bytes;
-
- function setUp() public {}
-
- function run() public {
- bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0);
- console2.logBytes(options);
- }
-}
-```
-
-The output of this script results in:
-
-```bash
-0x00030100110100000000000000000000000000030d40
-```
-
-For this tutorial, rather than building and generating your own message options, you can use the bytes output provided above.
-
-
-Covering all of the different message options in detail is out of scope for this tutorial. If you are interested in learning more about the different message options and how to build them, visit the [LayerZero developer documentation](https://docs.layerzero.network/v2/developers/evm/overview).
-
-
-### Estimating the gas fee
-
-Before you can send a message from your contract on Base Sepolia, you need to estimate the fee associated with sending the message. You can use the `cast` command to call the `estimateFee()` function of the `ExampleContract` contract.
-
-To estimate the gas fee for sending a message from Base Sepolia to Optimism Sepolia, run the following command:
-
-```bash
-cast send --rpc-url $BASE_SEPOLIA_RPC "estimateFee(uint32, string, bytes)" $OPTIMISM_SEPOLIA_LZ_ENDPOINT_ID "Hello World" 0x00030100110100000000000000000000000000030d40 --account deployer
-```
-
-
-Replace `` with the contract address of your deployed `ExampleContract` contract on Base Sepolia before running the provided `cast` command.
-
-
-The command above calls `estimateFee(uint32, string, bytes, bool)`, while providing the required arguments, including: the endpoint ID of the destination chain, the text to send, and the message options (generated in the last section).
-
-### Sending the message
-
-Once you have fetched the estimated gas for sending your message, you can now call `sendMessage` and provide the value returned as the `msg.value`.
-
-For example, to send a message from Base Sepolia to Optimism Sepolia with an estimated gas fee, run the following command:
-
-```bash
-cast send --rpc-url $BASE_SEPOLIA_RPC --value "sendMessage(uint32, string, bytes)" $OPTIMISM_SEPOLIA_LZ_ENDPOINT_ID "Hello World" 0x00030100110100000000000000000000000000030d40 --account deployer
-```
-
-
-Replace `` with the contract address of your deployed `ExampleContract` contract on Base Sepolia, and `` with the gas estimate (in wei) returned by the call to estimateFee, before running the provided `cast` command.
-
-
-You can view the status of your cross-chain transaction on [LayerZero Scan](https://layerzeroscan.com/).
-
-### Receiving the message
-
-Once the message has been sent and received on the destination chain, the \_Receive function will be called on the `
diff --git a/docs/learn/onchain-app-development/cross-chain/send-messages-and-tokens-from-base-chainlink.mdx b/docs/learn/onchain-app-development/cross-chain/send-messages-and-tokens-from-base-chainlink.mdx
deleted file mode 100644
index 2a688ff1e..000000000
--- a/docs/learn/onchain-app-development/cross-chain/send-messages-and-tokens-from-base-chainlink.mdx
+++ /dev/null
@@ -1,598 +0,0 @@
----
-title: Sending messages and tokens from Base to other chains using Chainlink CCIP
-description: A tutorial that teaches how to use Chainlink CCIP to perform cross-chain messaging and token transfers from Base Goerli testnet to Optimism Goerli testnet.
-authors:
- - taycaldwell
-tags: ['cross-chain']
-difficulty: intermediate
----
-
-# Sending messages and tokens from Base to other chains using Chainlink CCIP
-
-This tutorial will guide you through the process of sending messages and tokens from a Base smart contract to another smart contract on a different chain using Chainlink's Cross-chain Interoperability Protocol (CCIP).
-
-## Objectives
-
-By the end of this tutorial you should be able to do the following:
-
-- Set up a smart contract project for Base using Foundry
-- Install Chainlink CCIP as a dependency
-- Use Chainlink CCIP within your smart contract to send messages and/or tokens to contracts on other different chains
-- Deploy and test your smart contracts on Base testnet
-
-
-Chainlink CCIP is in an "Early Access" development stage, meaning some of the functionality described within this tutorial is under development and may change in later versions.
-
-
-## Prerequisites
-
-### Foundry
-
-This tutorial requires you to have Foundry installed.
-
-- From the command-line (terminal), run: `curl -L https://foundry.paradigm.xyz | bash`
-- Then run `foundryup`, to install the latest (nightly) build of Foundry
-
-For more information, see the Foundry Book [installation guide](https://book.getfoundry.sh/getting-started/installation).
-
-### Coinbase Wallet
-
-In order to deploy a smart contract, you will first need a wallet. You can create a wallet by downloading the Coinbase Wallet browser extension.
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-For this tutorial you will need to fund your wallet with both ETH and LINK on Base Goerli and Optimism Goerli.
-
-The ETH is required for covering gas fees associated with deploying smart contracts to the blockchain, and the LINK token is required to pay for associated fees when using CCIP.
-
-- To fund your wallet with ETH on Base Goerli, visit a faucet listed on the [Base Faucets](https://docs.base.org/base-chain/tools/network-faucets) page.
-- To fund your wallet with ETH on Optimism Goerli, visit a faucet listed on the [Optimism Faucets](https://docs.optimism.io/builders/tools/faucets) page.
-- To fund your wallet with LINK, visit the [Chainlink Faucet](https://faucets.chain.link/base-testnet).
-
-
-If you are interested in building on Mainnet, you will need to [apply for Chainlink CCIP mainnet access](https://chainlinkcommunity.typeform.com/ccip-form?#ref_id=ccip_docs).
-
-
----
-
-## What is Chainlink CCIP?
-
-Chainlink CCIP (Cross-chain Interoperability Protocol) provides a solution for sending message data and transferring tokens across different chains.
-
-The primary way for users to interface with Chainlink CCIP is through smart contracts known as [Routers](https://docs.chain.link/ccip/architecture#router). A Router contract is responsible for initiating cross-chain interactions.
-
-Users can interact with [Routers](https://docs.chain.link/ccip/architecture#router) to perform the following cross-chain capabilities:
-
-| Capability | Description | Supported receivers |
-| :--------------------------- | :------------------------------------------------------------------------------------------- | :---------------------- |
-| Arbitrary messaging | Send arbitrary (encoded) data from one chain to another. | Smart contracts only |
-| Token transfers | Send tokens from one chain to another. | Smart contracts or EOAs |
-| Programmable token transfers | Send tokens and arbitrary (encoded) data from one chain to another, in a single transaction. | Smart contracts only |
-
-
-Externally owned accounts (EOAs) on EVM blockchains are unable to receive message data, because of this, only smart contracts are supported as receivers when sending arbitrary messages or programmable token transfers. Any attempt to send a programmable token transfer (data and tokens) to an EOA, will result in only the tokens being received.
-
-
-### High-level concepts
-
-Although [Routers](https://docs.chain.link/ccip/architecture#router) are the primary interface users will interact with when using CCIP, this section will cover what happens after instructions for a cross-chain interaction are sent to a Router.
-
-#### OnRamps
-
-Once a Router receives an instruction for a cross-chain interaction, it passes it on to another contract known as an [OnRamp](https://docs.chain.link/ccip/architecture#onramp). OnRamps are responsible for a variety of tasks, including: verifying message size and gas limits, preserving the sequencing of messages, managing any fee payments, and interacting with the [token pool](https://docs.chain.link/ccip/architecture#token-pools) to `lock` or `burn` tokens if a token transfer is being made.
-
-#### OffRamps
-
-The destination chain will have a contract known as an [OffRamp](https://docs.chain.link/ccip/architecture#offramp). OffRamps are responsible for a variety of tasks, including: ensuring the authenticity of a message, making sure each transaction is only executed once, and transmitting received messages to the Router contract on the destination chain.
-
-#### Token pools
-
-A [token pool](https://docs.chain.link/ccip/architecture#token-pools) is an abstraction layer over ERC-20 tokens that facilitates OnRamp and OffRamp token-related operations. They are configured to use either a `Lock and Unlock` or `Burn and Mint` mechanism, depending on the type of token.
-
-For example, because blockchain-native gas tokens (i.e. ETH, MATIC, AVAX) can only be minted on their native chains, a `Lock and Mint` mechanism must be used. This mechanism locks the token at the source chain, and mints a synthetic asset on the destination chain.
-
-In contrast, tokens that can be minted on multiple chains (i.e. USDC, USDT, FRAX, etc.), token pools can use a `Burn and Mint` mechanism, where the token is burnt on the source chain and minted on the destination chain.
-
-#### Risk Management Network
-
-Between instructions for a cross-chain interaction making its way from an OnRamp on the source chain to an OffRamp on the destination chain, it will pass through the [Risk Management Network](https://docs.chain.link/ccip/concepts#risk-management-network).
-
-The Risk Management Network is a secondary validation service built using a variety of offchain and onchain components, with the responsibilities of monitoring all chains against abnormal activities.
-
-
-A deep-dive on the technical details of each of these components is too much to cover in this tutorial, but if interested you can learn more by visiting the [Chainlink documentation](https://docs.chain.link/ccip/architecture).
-
-
-## Creating a project
-
-Before you begin, you need to set up your smart contract development environment. You can setup a development environment using tools like [Hardhat](/learn/hardhat/hardhat-tools-and-testing/deploy-with-hardhat) or [Foundry](/learn/foundry/deploy-with-foundry). For this tutorial you will use Foundry.
-
-To create a new Foundry project, first create a new directory:
-
-```bash
-mkdir myproject
-```
-
-Then run:
-
-```bash
-cd myproject
-forge init
-```
-
-This will create a Foundry project with the following basic layout:
-
-```bash
-.
-├── foundry.toml
-├── script
-├── src
-└── test
-```
-
-
-You can delete the `src/Counter.sol`, `test/Counter.t.sol`, and `script/Counter.s.sol` boilerplate files that were generated with the project, as you will not be needing them.
-
-
-## Installing Chainlink smart contracts
-
-To use Chainlink CCIP within your Foundry project, you need to install Chainlink CCIP smart contracts as a project dependency using `forge install`.
-
-To install Chainlink CCIP smart contracts, run:
-
-```bash
-forge install smartcontractkit/ccip --no-commit
-```
-
-Once installed, update your `foundry.toml` file by appending the following line:
-
-```bash
-remappings = ['@chainlink/contracts-ccip/=lib/ccip/contracts']
-```
-
-## Writing the smart contracts
-
-The most basic use case for Chainlink CCIP is to send data and/or tokens between smart contracts on different blockchains.
-
-To accomplish this, in this tutorial, you will need to create two separate smart contracts:
-
-- `Sender` contract: A smart contract that interacts with CCIP to send data and tokens.
-- `Receiver` contract: A smart contract that interacts with CCIP to receive data and tokens.
-
-### Creating a Sender contract
-
-The code snippet below is for a basic smart contract that uses CCIP to send data:
-
-```solidity
-pragma solidity ^0.8.0;
-
-import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
-import {OwnerIsCreator} from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol";
-import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
-import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
-
-contract Sender is OwnerIsCreator {
-
- IRouterClient private router;
- IERC20 private linkToken;
-
- /// @notice Initializes the contract with the router and LINK token address.
- /// @param _router The address of the router contract.
- /// @param _link The address of the link contract.
- constructor(address _router, address _link) {
- router = IRouterClient(_router);
- linkToken = IERC20(_link);
- }
-
- /// @notice Sends data to receiver on the destination chain.
- /// @param destinationChainSelector The identifier (aka selector) for the destination blockchain.
- /// @param receiver The address of the recipient on the destination blockchain.
- /// @param text The string text to be sent.
- /// @return messageId The ID of the message that was sent.
- function sendMessage(
- uint64 destinationChainSelector,
- address receiver,
- string calldata text
- ) external onlyOwner returns (bytes32 messageId) {
- Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
- receiver: abi.encode(receiver), // Encode receiver address
- data: abi.encode(text), // Encode text to send
- tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array indicating no tokens are being sent
- extraArgs: Client._argsToBytes(
- Client.EVMExtraArgsV1({gasLimit: 200_000}) // Set gas limit
- ),
- feeToken: address(linkToken) // Set the LINK as the feeToken address
- });
-
- // Get the fee required to send the message
- uint256 fees = router.getFee(
- destinationChainSelector,
- message
- );
-
- // Revert if contract does not have enough LINK tokens for sending a message
- require(linkToken.balanceOf(address(this)) > fees, "Not enough LINK balance");
-
- // Approve the Router to transfer LINK tokens on contract's behalf in order to pay for fees in LINK
- linkToken.approve(address(router), fees);
-
- // Send the message through the router
- messageId = router.ccipSend(destinationChainSelector, message);
-
- // Return the messageId
- return messageId;
- }
-}
-```
-
-Create a new file under your project's `src/` directory named `Sender.sol` and copy the code above into the file.
-
-#### Code walkthrough
-
-The sections below provide a detailed explanation for the code for the `Sender` contract provided above.
-
-##### Initializing the contract
-
-In order to send data using CCIP, the `Sender` contract will need access to the following dependencies:
-
-1. **The `Router` contract**: This contract serves as the primary interface when using CCIP to send and receive messages and tokens.
-2. **The fee token contract**: This contract serves as the contract for the token that will be used to pay fees when sending messages and tokens. For this tutorial, the contract address for LINK token is used.
-
-The `Router` contract address and LINK token address are passed in as parameters to the contract's constructor and stored as member variables for later for sending messages and paying any associated fees.
-
-```solidity
-contract Sender is OwnerIsCreator {
-
- IRouterClient private router;
- IERC20 private linkToken;
-
- /// @notice Initializes the contract with the router and LINK token address.
- /// @param _router The address of the router contract.
- /// @param _link The address of the link contract.
- constructor(address _router, address _link) {
- router = IRouterClient(_router);
- linkToken = IERC20(_link);
- }
-```
-
-The `Router` contract provides two important methods that can be used when sending messages using CCIP:
-
-- `getFee`: Given a chain selector and message, returns the fee amount required to send the message.
-- `ccipSend`: Given a chain selector and message, sends the message through the router and returns an associated message ID.
-
-The next section describes how these methods are utilized to send a message to another chain.
-
-##### Sending a message
-
-The `Sender` contract defines a custom method named `sendMessage` that utilizes the methods described above in order to:
-
-1. Construct a message using the `EVM2AnyMessage` method provided by the `Client` CCIP library, using the following data:
- 1. `receiver`: The receiver contract address (encoded).
- 1. `data`: The text data to send with the message (encoded).
- 1. `tokenAmounts`: The amount of tokens to send with the message. For sending just an arbitrary message this field is defined as an empty array (`new Client.EVMTokenAmount[](0)`), indicating that no tokens will be sent.
- 1. `extraArgs`: Extra arguments associated with the message, such as `gasLimit`.
- 1. `feeToken`: The `address` of the token to be used for paying fees.
-1. Get the fees required to send the message using the `getFee` method provided by the `Router` contract.
-1. Check that the contract holds an adequate amount of tokens to cover the fee. If not, revert the transaction.
-1. Approve the `Router` contract to transfer tokens on the `Sender` contracts behalf in order to cover the fees.
-1. Send the message to a specified chain using the `Router` contract's `ccipSend` method.
-1. Return a unique ID associated with the sent message.
-
-```solidity
-/// @param receiver The address of the recipient on the destination blockchain.
-/// @param text The string text to be sent.
-/// @return messageId The ID of the message that was sent.
-function sendMessage(
- uint64 destinationChainSelector,
- address receiver,
- string calldata text
-) external onlyOwner returns (bytes32 messageId) {
- Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
- receiver: abi.encode(receiver), // Encode receiver address
- data: abi.encode(text), // Encode text to send
- tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array indicating no tokens are being sent
- extraArgs: Client._argsToBytes(
- Client.EVMExtraArgsV1({gasLimit: 200_000}) // Set gas limit
- ),
- feeToken: address(linkToken) // Set the LINK as the feeToken address
- });
-
- // Get the fee required to send the message
- uint256 fees = router.getFee(
- destinationChainSelector,
- message
- );
-
- // Revert if contract does not have enough LINK tokens for sending a message
- require(linkToken.balanceOf(address(this)) > fees, "Not enough LINK balance");
-
- // Approve the Router to transfer LINK tokens on contract's behalf in order to pay for fees in LINK
- linkToken.approve(address(router), fees);
- // Send the message through the router
- messageId = router.ccipSend(destinationChainSelector, message);
-
- // Return the messageId
- return messageId;
-}
-```
-
-### Creating a Receiver contract
-
-The code snippet below is for a basic smart contract that uses CCIP to receive data:
-
-```solidity
-pragma solidity ^0.8.0;
-
-import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
-import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
-
-contract Receiver is CCIPReceiver {
-
- bytes32 private _messageId;
- string private _text;
-
- /// @notice Constructor - Initializes the contract with the router address.
- /// @param router The address of the router contract.
- constructor(address router) CCIPReceiver(router) {}
-
- /// @notice Handle a received message
- /// @param message The cross-chain message being received.
- function _ccipReceive(
- Client.Any2EVMMessage memory message
- ) internal override {
- _messageId = message.messageId; // Store the messageId
- _text = abi.decode(message.data, (string)); // Decode and store the message text
- }
-
- /// @notice Gets the last received message.
- /// @return messageId The ID of the last received message.
- /// @return text The last received text.
- function getMessage()
- external
- view
- returns (bytes32 messageId, string memory text)
- {
- return (_messageId, _text);
- }
-}
-```
-
-Create a new file under your project's `src/` directory named `Receiver.sol` and copy the code above into the file.
-
-#### Code walkthrough
-
-The sections below provide a detailed explanation for the code for the `Receiver` contract provided above.
-
-##### Initializing the contract
-
-In order to receive data using CCIP, the `Receiver` contract will need to extend to the`CCIPReceiver` interface. Extending this interface allows the `Receiver` contract to initialize the contract with the router address from the constructor, as seen below:
-
-```solidity
-import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
-
-contract Receiver is CCIPReceiver {
-
- /// @notice Constructor - Initializes the contract with the router address.
- /// @param router The address of the router contract.
- constructor(address router) CCIPReceiver(router) {}
-}
-```
-
-##### Receiving a message
-
-Extending the `CCIPReceiver` interface also allows the `Receiver` contract to override the `_ccipReceive` handler method for when a message is received and define custom logic.
-
-```solidity
-/// @notice Handle a received message
-/// @param message The cross-chain message being received.
-function _ccipReceive(
- Client.Any2EVMMessage memory message
-) internal override {
- // Add custom logic here
-}
-```
-
-The `Receiver` contract in this tutorial provides custom logic that stores the `messageId` and `text` (decoded) as member variables.
-
-```solidity
-contract Receiver is CCIPReceiver {
-
- bytes32 private _messageId;
- string private _text;
-
- /// @notice Constructor - Initializes the contract with the router address.
- /// @param router The address of the router contract.
- constructor(address router) CCIPReceiver(router) {}
-
- /// @notice Handle a received message
- /// @param message The cross-chain message being received.
- function _ccipReceive(
- Client.Any2EVMMessage memory message
- ) internal override {
- _messageId = message.messageId; // Store the messageId
- _text = abi.decode(message.data, (string)); // Decode and store the message text
- }
-}
-```
-
-##### Retrieving a message
-
-The `Receiver` contract defines a custom method named `getMessage` that returns the details of the last received message `_messageId` and `_text`. This method can be called to fetch the message data details after the `_ccipReceive` receives a new message.
-
-```solidity
-/// @notice Gets the last received message.
-/// @return messageId The ID of the last received message.
-/// @return text The last received text.
-function getMessage()
- external
- view
- returns (bytes32 messageId, string memory text)
-{
- return (_messageId, _text);
-}
-```
-
-## Compiling the smart contracts
-
-To compile your smart contracts, run:
-
-```bash
-forge build
-```
-
-## Deploying the smart contract
-
-### Setting up your wallet as the deployer
-
-Before you can deploy your smart contract to the Base network, you will need to set up a wallet to be used as the deployer.
-
-To do so, you can use the [`cast wallet import`](https://getfoundry.sh/cast/reference/wallet/import/#cast-wallet-import) command to import the private key of the wallet into Foundry's securely encrypted keystore:
-
-```bash
-cast wallet import deployer --interactive
-```
-
-After running the command above, you will be prompted to enter your private key, as well as a password for signing transactions.
-
-
-For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key). **It is critical that you do NOT commit this to a public repo**.
-
-
-To confirm that the wallet was imported as the `deployer` account in your Foundry project, run:
-
-```bash
-cast wallet list
-```
-
-### Setting up environment variables
-
-To setup your environment, create an `.env` file in the home directory of your project, and add the RPC URLs, [CCIP chain selectors](https://docs.chain.link/ccip/supported-networks/v1_2_0/testnet), [CCIP router addresses](https://docs.chain.link/ccip/supported-networks/v1_2_0/testnet), and [LINK token addresses](https://docs.chain.link/resources/link-token-contracts) for both Base Goerli and Optimism Goerli testnets:
-
-```bash
-BASE_GOERLI_RPC="https://goerli.base.org"
-OPTIMISM_GOERLI_RPC="https://goerli.optimism.io"
-
-BASE_GOERLI_CHAIN_SELECTOR=5790810961207155433
-OPTIMISM_GOERLI_CHAIN_SELECTOR=2664363617261496610
-
-BASE_GOERLI_ROUTER_ADDRESS="0x80AF2F44ed0469018922c9F483dc5A909862fdc2"
-OPTIMISM_GOERLI_ROUTER_ADDRESS="0xcc5a0B910D9E9504A7561934bed294c51285a78D"
-
-BASE_GOERLI_LINK_ADDRESS="0x6D0F8D488B669aa9BA2D0f0b7B75a88bf5051CD3"
-OPTIMISM_GOERLI_LINK_ADDRESS="0xdc2CC710e42857672E7907CF474a69B63B93089f"
-```
-
-Once the `.env` file has been created, run the following command to load the environment variables in the current command line session:
-
-```bash
-source .env
-```
-
-### Deploying the smart contracts
-
-With your contracts compiled and environment setup, you are ready to deploy the smart contracts.
-
-To deploy a smart contract using Foundry, you can use the `forge create` command. The command requires you to specify the smart contract you want to deploy, an RPC URL of the network you want to deploy to, and the account you want to deploy with.
-
-
-Your wallet must be funded with ETH on the Base Goerli and Optimism Goerli to cover the gas fees associated with the smart contract deployment. Otherwise, the deployment will fail.
-
-To get testnet ETH for Base Goerli and Optimism Goerli, see the [prerequisites](#prerequisites).
-
-
-#### Deploying the Sender contract to Base Goerli
-
-To deploy the `Sender` smart contract to the Base Goerli testnet, run the following command:
-
-```bash
-forge create ./src/Sender.sol:Sender --rpc-url $BASE_GOERLI_RPC --constructor-args $BASE_GOERLI_ROUTER_ADDRESS $BASE_GOERLI_LINK_ADDRESS --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-After running the command above, the contract will be deployed on the Base Goerli test network. You can view the deployment status and contract by using a [block explorer](/base-chain/tools/block-explorers).
-
-#### Deploying the Receiver contract to Optimism Goerli
-
-To deploy the `Receiver` smart contract to the Optimism Goerli testnet, run the following command:
-
-```bash
-forge create ./src/Receiver.sol:Receiver --rpc-url $OPTIMISM_GOERLI_RPC --constructor-args $OPTIMISM_GOERLI_ROUTER_ADDRESS --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-After running the command above, the contract will be deployed on the Optimism Goerli test network. You can view the deployment status and contract by using the [OP Goerli block explorer](https://goerli-optimism.etherscan.io/).
-
-### Funding your smart contracts
-
-In order to pay for the fees associated with sending messages, the `Sender` contract will need to hold a balance of LINK tokens.
-
-Fund your contract directly from your wallet, or by running the following `cast` command:
-
-```bash
-cast send $BASE_GOERLI_LINK_ADDRESS --rpc-url $BASE_GOERLI_RPC "transfer(address,uint256)" `SENDER_CONTRACT_ADDRESS` 5 --account deployer
-```
-
-The above command sends `5` LINK tokens on Base Goerli testnet to the `Sender` contract.
-
-
-Replace `SENDER_CONTRACT_ADDRESS` with the contract address of your deployed Sender contract before running the provided cast command.
-
-
-## Interacting with the smart contract
-
-Foundry provides the `cast` command-line tool that can be used to interact with deployed smart contracts and call their functions.
-
-### Sending data
-
-The `cast` command can be used to call the `sendMessage(uint64, address, string)` function on the `Sender` contract deployed to Base Goerli in order to send message data to the `Receiver` contract on Optimism Goerli.
-
-To call the `sendMessage(uint64, address, string)` function of the `Sender` smart contract, run:
-
-```bash
-cast send `SENDER_CONTRACT_ADDRESS` --rpc-url $BASE_GOERLI_RPC "sendMessage(uint64, address, string)" $OPTIMISM_GOERLI_CHAIN_SELECTOR `RECEIVER_CONTRACT_ADDRESS` "Based" --account deployer
-```
-
-The command above calls the `sendMessage(uint64, address, string)` to send a message. The parameters passed in to the method include: The chain selector to the destination chain (Optimism Goerli), the `Receiver` contract address, and the text data to be included in the message (`Based`).
-
-
-Replace `SENDER_CONTRACT_ADDRESS` and `RECEIVER_CONTRACT_ADDRESS` with the contract addresses of your deployed Sender and Receiver contracts respectively before running the provided cast command.
-
-
-After running the command, a unique `messageId` should be returned.
-
-Once the transaction has been finalized, it will take a few minutes for CCIP to deliver the data to Optimism Goerli and call the `ccipReceive` function on the `Receiver` contract.
-
-
-You can use the [CCIP explorer](https://ccip.chain.link/) to see the status of the CCIP transaction.
-
-
-### Receiving data
-
-The `cast` command can also be used to call the `getMessage()` function on the `Receiver` contract deployed to Optimism Goerli in order to read the received message data.
-
-To call the `getMessage()` function of the `Receiver` smart contract, run:
-
-```bash
-cast send `RECEIVER_CONTRACT_ADDRESS` --rpc-url $OPTIMISM_GOERLI_RPC "getMessage()" --account deployer
-```
-
-
-Replace `RECEIVER_CONTRACT_ADDRESS` with the contract addresses of your deployed Receiver contract before running the provided cast command.
-
-
-After running the command, the `messageId` and `text` of the last received message should be returned.
-
-If the transaction fails, ensure the status of your `ccipSend` transaction has been finalized. You can using the [CCIP explorer](https://ccip.chain.link/).
-
-## Conclusion
-
-Congratulations! You have successfully learned how to perform cross-chain messaging on Base using Chainlink CCIP.
-
-To learn more about cross-chain messaging and Chainlink CCIP, check out the following resources:
-
-- [Cross-chain](https://docs.base.org/docs/tools/cross-chain)
-- [Chainlink CCIP](https://docs.chain.link/ccip)
diff --git a/docs/learn/onchain-app-development/deploy-with-fleek.mdx b/docs/learn/onchain-app-development/deploy-with-fleek.mdx
deleted file mode 100644
index ef1b599ea..000000000
--- a/docs/learn/onchain-app-development/deploy-with-fleek.mdx
+++ /dev/null
@@ -1,183 +0,0 @@
----
-title: 'Deploy an Onchain App with Fleek'
-description: Learn how to deploy an onchain app using Fleek.
-authors:
- - briandoyle81
-tags: ['frontend']
-difficulty: hard
-hide_table_of_contents: false
-
-image: https://docs.base.org/img/base-learn-open-graph.png
----
-
-# Deploy an Onchain App with Fleek
-
-One of the "secrets" of onchain apps is that they almost always have a very large web2 component that they're dependent on. Most onchain apps rely on traditional infrastructure for their frontends, APIs, and other parts of the architecture.
-
-[Fleek]'s goal is to address this issue with the [Fleek Network], a fast and trustless Content Delivery Network (CDN).
-
-In this tutorial, you'll use [Fleek] to deploy a site built with the [Onchain App Template].
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Deploy a [Next.js] using the Coinbase [Smart Wallet] on [Fleek]
-- Integrate with [Github] for CI/CD
-
-## Prerequisites
-
-### Next.js
-
-You should be familiar with [Next.js], but do not need to be an expert. If you are comfortable with other React libraries, the pattern should be relatively easy to follow.
-
-### Onchain Apps
-
-The tutorial assumes you're comfortable with the basics of deploying an app and connecting it to a smart contract. If you're still learning this part, check out our tutorials in [Base Learn] for [Building an Onchain App].
-
-## Setting up the Template
-
-You can skip this section if you've already built an app based off the template, such as our tutorial for [How to Mint on Zora with an App].
-
-Open [Onchain App Template], click the green `Use this template` button, and create a new repository from the template. Clone your repo and open it in an editor.
-
-Install _bun_ if you need to, and install dependencies.
-
-```bash
-# Install bun in case you don't have it
-curl -fsSL https://bun.sh/install | bash
-
-# Install packages
-bun i
-
-# Run Next app
-bun run dev
-```
-
-Navigate to `localhost:3000` and make sure that it's working, then shut down the server. For this tutorial, you **do not** need to set any environment variables.
-
-## Installing and Configuring Fleek
-
-[Fleek] requires static pages, so you'll need to ensure that your build process produces them. In your editor, open `next.config.js` and update the `nextConfig`.
-
-```javascript
-/** @type {import('next').NextConfig} */
-const nextConfig = {
- output: 'export',
- reactStrictMode: true,
- images: {
- unoptimized: true,
- },
- trailingSlash: true,
-};
-
-module.exports = nextConfig;
-```
-
-Save and close the file.
-
-Run `bun run build` and confirm that a directory called `out` is created.
-
-Navigate to [Fleek]'s website and create an account, or log in if you already have one.
-
-Click into `First Project`. You can rename it if you want in the `Settings` tab.
-
-The best way to start is to link Fleek to your repo from the beginning. Click `Add New` from the upper right corner, then select `Deploy My Site`. Select your code location, log into your Git provider, and accept installing the Fleek app.
-
-You can either give it permissions for all repos, or add them one at a time.
-
-Select your repo, and click the `Deploy` button. The `Configure Site` window should automatically populate with the appropriate information, but just in case:
-
-- Site Name: Your site name
-- Framework: Next.js
-- Branch: main
-- Publish Directory: out
-- Build Command: npm install && npm run build
-
-Click `Deploy Site`. Your deploy will probably fail, but this is expected!
-
-Return to your code editor.
-
-Open a terminal and install the Fleek CLI with:
-
-```bash
-npm install -g @fleek-platform/cli
-```
-
-Then, **in the root of your project** run:
-
-```bash
-fleek login
-```
-
-Click the link in your terminal, then click `Confirm` in the web page that opens up. Once you are connected, click the `Visit Dashboard` button. The site automatically creates a project called `First Project`. If you'd like, you can rename it, or add a new one.
-
-Each project can include more than one site.
-
-Return to your terminal in the app folder, and run:
-
-```bash
-fleek sites init
-```
-
-Select `First Project` from the list
-
-```
-⚠️ Warning! To proceed, please select a project...
-
-✔ Select a project from the list: › First Project
-
-✅ Success! You have switched to project "First Project".
-```
-
-For `We've found existing sites. Would you like to link to one of them?`, pick: `Y`
-
-Find the site you just added and select it.
-
-
-You're using TypeScript, but **do not** select `TypeScript (fleek.config.ts)` in the final prompt. Select `JSON (fleek.config.json)`.
-
-
-You'll get a few more prompts:
-
-- ? Please specify the directory containing the site files to be uploaded
- - Enter `out`
-- ? Would you like to include the optional "build" command?
- - Pick `Y`
-- ? Specify `build` command:
- - Enter `npm install && npm run build`
- - Select `JSON (fleek.config.json)`
-
-### Deployment
-
-You can deploy the site from the CLI as the docs describe, but you **do not need to**. There is a better way!
-
-```bash
-# Don't use, better method below!
-fleek sites deploy
-```
-
-Instead, trigger an automatic deploy by making a change to the text at `src/app/page.tsx`, committing your changes, and pushing to your repo.
-
-## Dashboard Overview and Confirming Deployment
-
-Return to your dashboard and click on the `Sites` tab. Click on the card for your new site to open it. Here, you can see information about your site in a similar presentation to other deployment providers.
-
-Click on the `<-> Deploys` tab and you'll see the automatic deploy you triggered by pushing the commit! Open your site by clicking on the build once it shifts from `Pending` to `Live`. You can then click on the link to view your site.
-
-Click on `Settings`. If you'd like, you can change the slug for your site to a name that's more related to your project.
-
-## Conclusion
-
-In this tutorial, you learned how to use [Fleek] to deploy a [Next.js] site based on [Onchain App Template]. You also learned how to link Fleek to your Git provider to enable CI/CD.
-
-[Base Learn]: https://base.org/learn
-[Smart Wallet]: https://www.smartwallet.dev/why
-[Fleek]: https://fleek.xyz
-[Fleek Network]: https://fleek.xyz/blog/announcements/introducing-fleek-network-and-fleek-xyz/
-[Next.js]: https://nextjs.org/
-[Onchain App Template]: https://github.com/coinbase/onchain-app-template
-[Smart Wallet]: https://www.coinbase.com/wallet/smart-wallet
-[How to Mint on Zora with an App]: /use-case-guides/creator/nft-minting-with-zora.mdx
-
-
diff --git a/docs/learn/onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds.mdx b/docs/learn/onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds.mdx
deleted file mode 100644
index 5803bbc1f..000000000
--- a/docs/learn/onchain-app-development/finance/access-real-time-asset-data-pyth-price-feeds.mdx
+++ /dev/null
@@ -1,244 +0,0 @@
----
-title: Accessing real-time asset data using Pyth Price Feeds
-slug: /oracles-pyth-price-feeds
-description: A tutorial that teaches how to use Pyth Price Feeds to access real-time asset data, directly from your smart contracts on the Base testnet.
-author: taycaldwell
----
-
-# Accessing real-time asset data using Pyth Price Feeds
-
-This tutorial will guide you through the process of creating a smart contract on Base that utilizes Pyth Network oracles to consume a price feed.
-
-## Objectives
-
-By the end of this tutorial you should be able to do the following:
-
-- Set up a smart contract project for Base using Foundry
-- Install the Pyth smart contracts
-- Consume a Pyth Network price feed within your smart contract
-- Deploy and test your smart contracts on Base
-
-## Prerequisites
-
-### Foundry
-
-This tutorial requires you to have Foundry installed.
-
-- From the command-line (terminal), run: `curl -L https://foundry.paradigm.xyz | bash`
-- Then run `foundryup`, to install the latest (nightly) build of Foundry
-
-For more information, see the Foundry Book [installation guide](https://book.getfoundry.sh/getting-started/installation).
-
-### Coinbase Wallet
-
-In order to deploy a smart contract, you will first need a wallet. You can create a wallet by downloading the Coinbase Wallet browser extension.
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-Deploying contracts to the blockchain requires a gas fee. Therefore, you will need to fund your wallet with ETH to cover those gas fees.
-
-For this tutorial, you will be deploying a contract to the Base Sepolia test network. You can fund your wallet with Base Sepolia ETH using one of the faucets listed on the Base [Network Faucets](https://docs.base.org/base-chain/tools/network-faucets) page.
-
-## What is Pyth Network?
-
-**Pyth Network** focuses on ultra-low latency and real-time data, making it suitable for financial applications that require sub-second updates. Pyth's design emphasizes performance, and it is designed to provide data for a range of traditional and DeFi assets.
-
-## Creating a project
-
-Before you can begin writing smart contracts for Base and consuming Pyth price feeds, you need to set up your development environment by creating a Foundry project.
-
-To create a new Foundry project, first create a new directory:
-
-```bash
-mkdir myproject
-```
-
-Then run:
-
-```bash
-cd myproject
-forge init
-```
-
-This will create a Foundry project, which has the following basic layout:
-
-```bash
-.
-├── foundry.toml
-├── script
- │ └── Counter.s.sol
-├── src
- │ └── Counter.sol
-└── test
- └── Counter.t.sol
-```
-
-## Installing Pyth smart contracts
-
-To use Pyth price feeds within your project, you need to install Pyth oracle contracts as a project dependency using `forge install`.
-
-To install Pyth oracle contracts, run:
-
-```bash
-forge install pyth-network/pyth-sdk-solidity@v2.2.0 --no-git --no-commit
-```
-
-Once installed, update your `foundry.toml` file by appending the following line:
-
-```bash
-remappings = ['@pythnetwork/pyth-sdk-solidity/=lib/pyth-sdk-solidity']
-```
-
-## Writing and compiling the Smart Contract
-
-Once your project has been created and dependencies have been installed, you can now start writing a smart contract.
-
-The Solidity code below defines a smart contract named `ExampleContract`. The code uses the `IPyth` interface from the [Pyth Solidity SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/target_chains/ethereum/sdk/solidity).
-
-An instance of`IPyth` is defined within the contract that provides functions for consuming Pyth price feeds. The constructor for the `IPyth` interface expects a contract address to be provided. This address provided in the code example below (`0xA2aa501b19aff244D90cc15a4Cf739D2725B5729`) corresponds to the Pyth contract address for the Base Sepolia testnet.
-
-
-Pyth also supports other EVM networks, such as Base Mainnet. For a list of all network contract addresses, visit the [Pyth documentation](https://docs.pyth.network/documentation/pythnet-price-feeds/evm).
-
-
-The contract also contains a function named `getLatestPrice`. This function takes a provided `priceUpdateData` that is used to get updated price data, and returns the price given a `priceId` of a price feed. The smart contract provided below uses a `priceId` of `0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace`, which corresponds to the price feed for `ETH / USD`.
-
-
-Pyth provides a number of price feeds. For a list of available price feeds, visit the [Pyth documentation](https://pyth.network/developers/price-feed-ids#pyth-evm-stable).
-
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
-import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
-
-contract ExampleContract {
- IPyth pyth;
-
- /**
- * Network: Base Sepolia (testnet)
- * Address: 0xA2aa501b19aff244D90cc15a4Cf739D2725B5729
- */
- constructor() {
- pyth = IPyth(0xA2aa501b19aff244D90cc15a4Cf739D2725B5729);
- }
-
- function getLatestPrice(
- bytes[] calldata priceUpdateData
- ) public payable returns (PythStructs.Price memory) {
- // Update the prices to the latest available values and pay the required fee for it. The `priceUpdateData` data
- // should be retrieved from our off-chain Price Service API using the `pyth-evm-js` package.
- // See section "How Pyth Works on EVM Chains" below for more information.
- uint fee = pyth.getUpdateFee(priceUpdateData);
- pyth.updatePriceFeeds{ value: fee }(priceUpdateData);
-
- bytes32 priceID = 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;
- // Read the current value of priceID, aborting the transaction if the price has not been updated recently.
- // Every chain has a default recency threshold which can be retrieved by calling the getValidTimePeriod() function on the contract.
- // Please see IPyth.sol for variants of this function that support configurable recency thresholds and other useful features.
- return pyth.getPrice(priceID);
- }
-}
-```
-
-In your project, add the code provided above to a new file named `src/ExampleContract.sol` and delete the `src/Counter.sol` contract that was generated with the project (You can also delete the `test/Counter.t.sol` and `script/Counter.s.sol` files).
-
-To compile the new smart contract, run:
-
-```bash
-forge build
-```
-
-## Deploying the smart contract
-
-### Setting up your wallet as the deployer
-
-Before you can deploy your smart contract to the Base network, you will need to set up a wallet to be used as the deployer.
-
-To do so, you can use the [`cast wallet import`](https://getfoundry.sh/cast/reference/wallet/import/#cast-wallet-import) command to import the private key of the wallet into Foundry's securely encrypted keystore:
-
-```bash
-cast wallet import deployer --interactive
-```
-
-After running the command above, you will be prompted to enter your private key, as well as a password for signing transactions.
-
-
-For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key).
-
-**It is critical that you do NOT commit this to a public repo**.
-
-
-To confirm that the wallet was imported as the `deployer` account in your Foundry project, run:
-
-```bash
-cast wallet list
-```
-
-### Setting up environment variables for Base Sepolia
-
-To setup your environment for deploying to the Base network, create an `.env` file in the home directory of your project, and add the RPC URL for the Base Sepolia testnet:
-
-```
-BASE_SEPOLIA_RPC="https://sepolia.base.org"
-```
-
-Once the `.env` file has been created, run the following command to load the environment variables in the current command line session:
-
-```bash
-source .env
-```
-
-### Deploying the smart contract to Base Sepolia
-
-With your contract compiled and environment setup, you are ready to deploy the smart contract to the Base Sepolia Testnet!
-
-For deploying a single smart contract using Foundry, you can use the `forge create` command. The command requires you to specify the smart contract you want to deploy, an RPC URL of the network you want to deploy to, and the account you want to deploy with.
-
-To deploy the `ExampleContract` smart contract to the Base Sepolia test network, run the following command:
-
-```bash
-forge create ./src/ExampleContract.sol:ExampleContract --rpc-url $BASE_SEPOLIA_RPC --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-
-Your wallet must be funded with ETH on the Base Sepolia Testnet to cover the gas fees associated with the smart contract deployment. Otherwise, the deployment will fail.
-
-To get testnet ETH for Base Sepolia, see the [prerequisites](#prerequisites).
-
-
-After running the command above, the contract will be deployed on the Base Sepolia test network. You can view the deployment status and contract by using a [block explorer](/base-chain/tools/block-explorers).
-
-## Interacting with the Smart Contract
-
-The `getLatestPrice(bytes[])` function of the deployed contract takes a `priceUpdateData` argument that is used to get the latest price. This data can be fetched using the Hermes web service. Hermes allows users to easily query for recent price updates via a REST API. Make a curl request to fetch the `priceUpdateData` the `priceId`, `0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace`:
-
-```
-curl https://hermes.pyth.network/api/latest_vaas?ids[]=0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace
-```
-
-Once you have the `priceUpdateData`, you can use Foundry's `cast` command-line tool to interact with the smart contract and call the `getLatestPrice(bytes[])` function to fetch the latest price of ETH.
-
-To call the `getLatestPrice(bytes[])` function of the smart contract, run the following command, replacing `` with the address of your deployed contract, and `` with the `priceUpdateData` returned by the Hermes endpoint:
-
-```bash
-cast call --rpc-url $BASE_SEPOLIA_RPC "getLatestPrice(bytes[])"
-```
-
-You should receive the latest `ETH / USD` price in hexadecimal form.
-
-## Conclusion
-
-Congratulations! You have successfully deployed and interacted with a smart contract that consumes a Pyth Network oracle to access a real-time price feed on Base.
-
-To learn more about Oracles and using Pyth Network price feeds within your smart contracts on Base, check out the following resources:
-
-- [Oracles](https://docs.base.org/chain/oracles)
-- [Pyth Network Price Feeds](https://docs.pyth.network/documentation/pythnet-price-feeds/evm)
-
diff --git a/docs/learn/onchain-app-development/finance/access-real-world-data-chainlink.mdx b/docs/learn/onchain-app-development/finance/access-real-world-data-chainlink.mdx
deleted file mode 100644
index 8f018f941..000000000
--- a/docs/learn/onchain-app-development/finance/access-real-world-data-chainlink.mdx
+++ /dev/null
@@ -1,229 +0,0 @@
----
-title: Accessing real-world data using Chainlink Data Feeds
-slug: /oracles-chainlink-price-feeds
-description: A tutorial that teaches how to use Chainlink Data Feeds to access real-world data, such as asset prices, directly from your smart contracts on the Base testnet.
-author: taycaldwell
----
-
-# Accessing real-world data using Chainlink Data Feeds
-
-This tutorial will guide you through the process of creating a smart contract on Base that utilizes Chainlink Data Feeds to access real-world data, such as asset prices, directly from your smart contracts.
-
-## Objectives
-
-By the end of this tutorial you should be able to do the following:
-
-- Set up a smart contract project for Base using Foundry
-- Install the Chainlink smart contracts
-- Consume a Chainlink price data feed within your smart contract
-- Deploy and test your smart contracts on Base
-
-## Prerequisites
-
-### Foundry
-
-This tutorial requires you to have Foundry installed.
-
-- From the command-line (terminal), run: `curl -L https://foundry.paradigm.xyz | bash`
-- Then run `foundryup`, to install the latest (nightly) build of Foundry
-
-For more information, see the Foundry Book [installation guide](https://book.getfoundry.sh/getting-started/installation).
-
-### Coinbase Wallet
-
-In order to deploy a smart contract, you will first need a wallet. You can create a wallet by downloading the Coinbase Wallet browser extension.
-
-- Download [Coinbase Wallet](https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en)
-
-### Wallet funds
-
-Deploying contracts to the blockchain requires a gas fee. Therefore, you will need to fund your wallet with ETH to cover those gas fees.
-
-For this tutorial, you will be deploying a contract to the Base Goerli test network. You can fund your wallet with Base Goerli ETH using one of the faucets listed on the Base [Network Faucets](https://docs.base.org/base-chain/tools/network-faucets) page.
-
-## What are Chainlink Data Feeds?
-
-Accurate price data is essential in DeFi applications. However, blockchain networks lack the capability to directly fetch external real-world data, leading to the "[Oracle Problem](https://chain.link/education-hub/oracle-problem)".
-
-Chainlink Data Feeds offer a solution to this problem by serving as a secure middleware layer that bridges the gap between real-world asset prices and onchain smart contracts.
-
-## Creating a project
-
-Before you can begin writing smart contracts for Base and consuming Chainlink data feeds, you need to set up your development environment by creating a Foundry project.
-
-To create a new Foundry project, first create a new directory:
-
-```bash
-mkdir myproject
-```
-
-Then run:
-
-```bash
-cd myproject
-forge init
-```
-
-This will create a Foundry project, which has the following basic layout:
-
-```bash
-.
-├── foundry.toml
-├── script
- │ └── Counter.s.sol
-├── src
- │ └── Counter.sol
-└── test
- └── Counter.t.sol
-```
-
-## Installing Chainlink smart contracts
-
-To use Chainlink's data feeds within your project, you need to install Chainlink smart contracts as a project dependency using `forge install`.
-
-To install Chainlink smart contracts, run:
-
-```bash
-forge install smartcontractkit/chainlink --no-commit
-```
-
-Once installed, update your `foundry.toml` file by appending the following line:
-
-```bash
-remappings = ['@chainlink/contracts/=lib/chainlink/contracts']
-```
-
-## Writing and compiling the Smart Contract
-
-Once your project has been created and dependencies have been installed, you can now start writing a smart contract.
-
-The Solidity code below defines a smart contract named `DataConsumerV3`. The code uses the `AggregatorV3Interface` interface from the [Chainlink contracts library](https://docs.chain.link/data-feeds/api-reference#aggregatorv3interface) to provide access to price feed data.
-
-The smart contract passes an address to `AggregatorV3Interface`. This address (`0xcD2A119bD1F7DF95d706DE6F2057fDD45A0503E2`) corresponds to the `ETH/USD` price feed on the Base Goerli network.
-
-
-Chainlink provides a number of price feeds for Base. For a list of available price feeds on Base, visit the [Chainlink documentation](https://docs.chain.link/data-feeds/price-feeds/addresses/?network=base&page=1).
-
-
-```solidity
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.0;
-
- import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
-
- contract DataConsumerV3 {
- AggregatorV3Interface internal priceFeed;
-
- /**
- * Network: Base Goerli
- * Aggregator: ETH/USD
- * Address: 0xcD2A119bD1F7DF95d706DE6F2057fDD45A0503E2
- */
- constructor() {
- priceFeed = AggregatorV3Interface(0xcD2A119bD1F7DF95d706DE6F2057fDD45A0503E2);
- }
-
- function getLatestPrice() public view returns (int) {
- (
- /* uint80 roundID */,
- int price,
- /* uint startedAt */,
- /* uint timeStamp */,
- /* uint80 answeredInRound */
- ) = priceFeed.latestRoundData();
- return price;
- }
- }
-```
-
-In your project, add the code provided above to a new file named `src/DataConsumerV3.sol`, and delete the `src/Counter.sol` contract that was generated with the project. (you can also delete the `test/Counter.t.sol` and `script/Counter.s.sol` files).
-
-To compile the new smart contract, run:
-
-```bash
-forge build
-```
-
-## Deploying the smart contract
-
-### Setting up your wallet as the deployer
-
-Before you can deploy your smart contract to the Base network, you will need to set up a wallet to be used as the deployer.
-
-To do so, you can use the [`cast wallet import`](https://getfoundry.sh/cast/reference/wallet/import/#cast-wallet-import) command to import the private key of the wallet into Foundry's securely encrypted keystore:
-
-```bash
-cast wallet import deployer --interactive
-```
-
-After running the command above, you will be prompted to enter your private key, as well as a password for signing transactions.
-
-
-For instructions on how to get your private key from Coinbase Wallet, visit the [Coinbase Wallet documentation](https://docs.cloud.coinbase.com/wallet-sdk/docs/developer-settings#show-private-key).
-
-**It is critical that you do NOT commit this to a public repo**.
-
-
-To confirm that the wallet was imported as the `deployer` account in your Foundry project, run:
-
-```bash
-cast wallet list
-```
-
-### Setting up environment variables for Base Goerli
-
-To setup your environment for deploying to the Base network, create an `.env` file in the home directory of your project, and add the RPC URL for the Base Goerli testnet:
-
-```
-BASE_GOERLI_RPC="https://goerli.base.org"
-```
-
-Once the `.env` file has been created, run the following command to load the environment variables in the current command line session:
-
-```bash
-source .env
-```
-
-### Deploying the smart contract to Base Goerli
-
-With your contract compiled and environment setup, you are ready to deploy the smart contract to the Base Goerli Testnet!
-
-For deploying a single smart contract using Foundry, you can use the `forge create` command. The command requires you to specify the smart contract you want to deploy, an RPC URL of the network you want to deploy to, and the account you want to deploy with.
-
-To deploy the `DataConsumerV3` smart contract to the Base Goerli test network, run the following command:
-
-```bash
-forge create ./src/DataConsumerV3.sol:DataConsumerV3 --rpc-url $BASE_GOERLI_RPC --account deployer
-```
-
-When prompted, enter the password that you set earlier, when you imported your wallet's private key.
-
-
-Your wallet must be funded with ETH on the Base Goerli Testnet to cover the gas fees associated with the smart contract deployment. Otherwise, the deployment will fail.
-
-To get testnet ETH for Base Goerli, see the [prerequisites](#prerequisites).
-
-
-After running the command above, the contract will be deployed on the Base Goerli test network. You can view the deployment status and contract by using a [block explorer](/base-chain/tools/block-explorers).
-
-## Interacting with the Smart Contract
-
-Foundry provides the `cast` command-line tool that can be used to interact with the smart contract that was deployed and call the `getLatestPrice()` function to fetch the latest price of ETH.
-
-To call the `getLatestPrice()` function of the smart contract, run:
-
-```bash
-cast call --rpc-url $BASE_GOERLI_RPC "getLatestPrice()"
-```
-
-You should receive the latest `ETH / USD` price in hexadecimal form.
-
-## Conclusion
-
-Congratulations! You have successfully deployed and interacted with a smart contract that consumes a Chainlink price feed on the Base blockchain network.
-
-To learn more about Oracles and using Chainlink to access real-world data within your smart contracts on Base, check out the following resources:
-
-- [Oracles](https://docs.base.org/chain/oracles)
-- [Chainlink Data Feeds on Base](https://docs.chain.link/data-feeds/price-feeds/addresses?network=base&page=1&search=#networks)
-
diff --git a/docs/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app.mdx b/docs/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app.mdx
deleted file mode 100644
index e5323108c..000000000
--- a/docs/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app.mdx
+++ /dev/null
@@ -1,169 +0,0 @@
----
-title: Add an In-App Onramp with OnchainKit
-description: Learn how to create a app that detects if a smart wallet has ETH and prompts users to add funds if needed.
-authors: [hughescoin]
----
-
-# Add an In-App Onramp with OnchainKit
-
-In this tutorial, you'll learn how to build an onchain app that checks a user's wallet balance and either allows them to mint an NFT or prompts them to add funds. We'll use the OnchainKit App Template as a starting point.
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Set up a project using the Onchain Kit App Template
-- Configure the app for to onboard users easily using [Smart Wallets]
-- Implement balance checking and conditional rendering
-- Use the Fund component to allow users to add funds to their wallet
-
-## Prerequisites
-
-### React and TypeScript
-
-You should be familiar with React and TypeScript. If you're new to these technologies, consider reviewing their [official documentation] first.
-
-### OnchainKit
-
-This tutorial uses Coinbase's Onchain Kit. Familiarity with its basic concepts will be helpful.
-
-### Access to the Coinbase Developer Platform
-
-You'll need to set up an account on with [Coinbase Developer Platform (CDP) Account](https://www.coinbase.com/cloud). The CDP provides various tools and services for blockchain development, including access to API endpoints and other resources that will be instrumental in your project. Once you've created your account, you'll be ready to move forward with integrating these services into your application.
-
-
-**CDP Configurations:**
-
-If you see a "something went wrong" error message when navigating to pay.coinbase.com, make sure you have "enforce secure initialization" disabled on the [Onramp config page] in Coinbase Developer Platform Dashboard.
-
-
-
-
-
-
-## Setting up the Project
-
-To get started, clone the Onchain Kit App Template by running
-
-```bash
-git clone git@github.com:coinbase/onchain-app-template.git
-```
-
-in your terminal, then navigate into the project directory with:
-
-```bash
-cd onchain-app-template
-```
-
-Next, install the necessary dependencies by executing `bun install` followed by `bun install viem`.
-
-After setting up the project, you'll need to configure your environment variables. Create a `.env` file in the root directory of your project and add the following line: `NEXT_PUBLIC_WC_PROJECT_ID=your_project_id_here`. Remember to replace 'your_project_id_here' with your actual project ID. Additionally, don't forget to configure your apiKey in the `src/app/components/OnchainProviders.tsx` file.
-
-## Configuring for Smart Wallets
-
-To make the app work only with smart wallets, modify `src/wagmi.ts`:
-
-```typescript
-// Inside the useWagmiConfig() function, before the useMemo() hook
-coinbaseWallet.preference = 'smartWalletOnly';
-```
-
-## Implementing Balance Checking
-
-Now well implement a check on the user's wallet to see if they have enough funds. Before we implement this check, let's create a helper function that grabs the user's Ethereum balance using [viem]. To do so, create a `utils.ts` file in the `src` directory that creates a client connected to Base and fetches the user's ETH balance:
-
-```typescript
-import { createPublicClient, http } from 'viem';
-import { base } from 'viem/chains';
-import type { GetBalanceParameters } from 'viem';
-
-const publicClient = createPublicClient({
- transport: http(),
- chain: base,
-});
-
-export async function getBalance(address: GetBalanceParameters) {
- const balance = publicClient.getBalance(address);
- return balance;
-}
-```
-
-Next, import the `getBalance()` function into your main component file (e.g., `src/app/page.tsx`). You will want to add a few [react hooks] to fetch the balance and store it as a state variable. Add the following lines of code to your `page.tsx` file:
-
-```typescript
-import { useState, useEffect } from 'react';
-import { getBalance } from '../utils';
-import { FundButton } from '@coinbase/onchainkit/fund';
-
-// Inside your component
-const [walletBalance, setWalletBalance] = useState('');
-
-useEffect(() => {
- async function fetchBalance() {
- if (address) {
- const balance = await getBalance({ address });
- setWalletBalance(String(balance));
- }
- }
- fetchBalance();
-}, [address]);
-```
-
-## Implementing Conditional Rendering
-
-Now that we know the user's balance, we can then have them mint an NFT or prompt them to fund their wallet if they do not have enough ETH.
-
-The end state is to show their balance along with the appropriate call to actions like so:
-
-
-
-
-
-Update your component's return statement with the following code:
-
-```typescript
-return (
-
-);
-```
-
-Sweet! Now our conditional rendering is in full force. If a user clicks on the `+ Add funds to transact` button they will be given three options for topping up their smart wallet:
-
-
-
-
-
-## Conclusion
-
-Congratulations! You've built a app that checks a user's smart wallet balance and provides appropriate options based on their funds.
-This app can serve as a foundation for more complex onchain applications that require users to have funded smart wallets.
-
-[Onchain Kit]: https://github.com/coinbase/onchainkit
-[Viem]: https://viem.sh/
-[Smart Wallets]: https://keys.coinbase.com/onboarding
-[viem]: https://viem.sh/docs/introduction
-[react hooks]: https://react.dev/reference/react/hooks
-[Onramp config page]: https://portal.cdp.coinbase.com/products/onramp
-[official documentation]: https://react.dev/
-
diff --git a/docs/learn/onchain-app-development/frontend-setup/building-an-onchain-app.mdx b/docs/learn/onchain-app-development/frontend-setup/building-an-onchain-app.mdx
deleted file mode 100644
index 99ddd07f9..000000000
--- a/docs/learn/onchain-app-development/frontend-setup/building-an-onchain-app.mdx
+++ /dev/null
@@ -1,219 +0,0 @@
----
-title: Building an Onchain App
-description: Learn step-by-step how to turn a regular template app into an onchain app with a wallet connection.
-hide_table_of_contents: false
----
-
-import { Danger } from "/snippets/danger.mdx";
-
-# Building an Onchain App
-
-While it's convenient and fast to start from a template, the template may not fit your needs. Whether you prefer a different stack, or have already started building the traditional web components of your app, it's common to need to manually add onchain libraries to get your app working.
-
-In this guide, you'll build the beginnings of an app similar to the one created by the [RainbowKit] quick start, but you'll do it piece by piece. You can follow along, and swap out any of our library choices with the ones you prefer.
-
----
-
-## Objectives
-
-By the end of this guide you should be able to:
-
-- Identify the role of a wallet aggregator in an onchain app
-- Debate the pros and cons of using a template
-- Add a wallet connection to a standard template app
-
----
-
-## Creating the Traditional App
-
-Start by running the [Next.js] script to create a Next.js app:
-
-```bash
-npx create-next-app@latest --use-yarn
-```
-
-This script will accept `.`, if you want to add the project to the root of a folder you've already created. Otherwise, name your project. Select each option in the generation script as you see fit. We recommend the following selections:
-
-- Use Typescript?: Yes
-- Use ESLint?: Yes
-- Use Tailwind?: Your preference
-- Use `src/` directory?: Yes
-- Use App Router?: Yes
-- Customize the default import alias?: No
-
-
-The default Next.js script installs [Tailwind]. [RainbowKit]'s does not.
-
-
-
-Run your app with `yarn dev` to make sure it generated correctly.
-
-### Manually Installing RainbowKit, Wagmi, and Viem
-
-The [quick start] guide for RainbowKit also contains step-by-step instructions for manual install. You'll be following an adjusted version here. Most of the setup is actually for configuring [wagmi], which sits on top of [viem] and makes it much easier to write React that interacts with the blockchain.
-
-Start by installing the dependencies:
-
-```bash
-npm install @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query
-```
-
-
-Onchain libraries and packages tend to require very current versions of Node. If you're not already using it, you may want to install [nvm].
-
-
-
-## Adding Imports, Connectors, Config
-
-In Next.js with the app router, the root of your app is found in `app/layout.tsx`, if you followed the recommended setup options. As you want the blockchain provider context to be available for the entire app, you'll add it here.
-
-You'll need to set up your providers in a second file, so that you can add `'use client';` to the top. Doing so forces this code to be run client side, which is necessary since your server won't have access to your users' wallet information.
-
-
-You must configure these wrappers in a separate file. It will not work if you try to add them and `'use client';` directly in `layout.tsx`!
-
-
-
-Add a new file in the `app` folder called `providers.tsx`.
-
-### Imports
-
-As discussed above, add `'use client';` to the top of the file.
-
-Continue with the imports:
-
-```tsx
-import '@rainbow-me/rainbowkit/styles.css';
-import { useState, type ReactNode } from 'react';
-import { getDefaultConfig, RainbowKitProvider } from '@rainbow-me/rainbowkit';
-import { WagmiProvider } from 'wagmi';
-import { base, baseSepolia } from 'wagmi/chains';
-import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
-```
-
-
-If you're adapting this guide to a different set of libraries or platforms, you may need to import `styles.css` differently. You'll know this is the case if you get ugly text at the bottom of the page instead of a nice modal when you click the connect button.
-
-
-
-### Config
-
-Now, you need to configure the chains, wallet connectors, and providers for your app. You'll use `getDefaultConfig` for now, to get started. See our guide on [Connecting to the Blockchain] for more information on blockchain providers.
-
-
-To take advantage of a more advanced set of options with [OnchainKit], see our tutorial on how to [Use the Coinbase Smart Wallet and EOAs with OnchainKit]. If you just want to customize the list of wallets in [RainbowKit], see our tutorial for [Coinbase Smart Wallet with RainbowKit].
-
-
-
-You'll need a `projectId` from [Wallet Connect Cloud], which you can get for free on their site. Make sure to insert it in the appropriate place.
-
-
-Remember, everything on the frontend is public! Be sure to configure the allowlist for your WalletConnect id!
-
-
-
-```tsx
-const config = getDefaultConfig({
- appName: 'Cool Onchain App',
- projectId: 'YOUR_PROJECT_ID',
- chains: [base, baseSepolia],
- ssr: true, // If your dApp uses server side rendering (SSR)
-});
-```
-
-### Returning the Context Providers
-
-[TanStack Query] is now a required dependency for wagmi, and you need to add it as a React context provider. The short version is that it helps with state management. Read the docs for the long version!
-
-Add an exported function for the providers. This sets up the `QueryClient` and returns `props.children` wrapped in all of your providers.
-
-```tsx
-export function Providers(props: { children: ReactNode }) {
- const [queryClient] = useState(() => new QueryClient());
-
- return (
-
-
- {props.children}
-
-
- );
-}
-```
-
-## Using Your new Providers
-
-Open `layout.tsx`. Import your `Providers`, being careful if you use auto-import as there are many other things with similar names in the list. Wrap the `children` in your `return` with the new `Providers`.
-
-```tsx
-return (
-
-
- {children}
-
-
-);
-```
-
-## Adding the Connect Button
-
-You're now ready to add your connect button. You can do this anywhere in your app, thanks to the `RainbowKitProvider`. Common practice would be to place the button in your app's header. Since the Next.js template doesn't have one, you can just add it to the top of the automatically generated page, rather than spending time implementing React components.
-
-Open up `page.tsx`, and import the `ConnectButton`:
-
-```tsx
-import { ConnectButton } from '@rainbow-me/rainbowkit';
-```
-
-Then, simply add the `ConnectButton` component at the top of the first `
`:
-
-```tsx
-// This function has been simplified to save space.
-export default function Home() {
- return (
-
-
-
-
- {/* Other Code...*/}
-
-
-
- );
-}
-```
-
-Run your app with `yarn dev`, and you should be able to use the RainbowKit connect button to connect with your wallet and switch between networks.
-
-You use the [Connect Button] props to modify its properties, or you can [customize the connect button] extensively. Some users dislike having the connect button display their token balance. Try disabling it with:
-
-```tsx
-
-```
-
----
-
-## Conclusion
-
-In this guide, you've learned how to assemble your onchain app from several pieces. You can use this knowledge to integrate a wallet connection with an existing site, or adjust the stack to meet your preferences. Finally, you've learned how to insert and customize the connect button.
-
-If you're looking to quickly bootstrap a simple app, you can always use a script, such as the RainbowKit [quick start]. If you're looking for a robust start for a consumer application, check out [OnchainKit]!
-
----
-
-[RainbowKit]: https://www.rainbowkit.com/
-[wagmi]: https://wagmi.sh/
-[viem]: https://viem.sh/
-[quick start]: https://www.rainbowkit.com/docs/installation
-[Next.js]: https://nextjs.org/
-[Tailwind]: https://tailwindcss.com/
-[nvm]: https://github.com/nvm-sh/nvm
-[WalletConnect]: https://cloud.walletconnect.com/
-[Connecting to the Blockchain]: https://docs.base.org/connecting-to-the-blockchain/overview
-[Wallet Connect Cloud]: https://cloud.walletconnect.com/
-[Connect Button]: https://www.rainbowkit.com/docs/connect-button
-[customize the connect button]: https://www.rainbowkit.com/docs/custom-connect-button
-[TanStack Query]: https://tanstack.com/query/latest
-[Coinbase Smart Wallet with RainbowKit]: https://docs.base.org/tutorials/smart-wallet-and-rainbowkit
-[OnchainKit]: https://onchainkit.xyz/?utm_source=basedocs&utm_medium=tutorials&campaign=building-an-onchain-app
-[Use the Coinbase Smart Wallet and EOAs with OnchainKit]: https://docs.base.org/tutorials/smart-wallet-and-eoa-with-onchainkit
diff --git a/docs/learn/onchain-app-development/frontend-setup/introduction-to-providers.mdx b/docs/learn/onchain-app-development/frontend-setup/introduction-to-providers.mdx
deleted file mode 100644
index 80ef241c9..000000000
--- a/docs/learn/onchain-app-development/frontend-setup/introduction-to-providers.mdx
+++ /dev/null
@@ -1,250 +0,0 @@
----
-title: 'Introduction to Providers'
-slug: /intro-to-providers
-description: A tutorial that teaches what providers are, why you need one, and how to configure several providers and use them to connect to the blockchain.
-author: briandoyle81
----
-
-# Introduction to Providers
-
-This tutorial provides an introduction to providers and shows you how to connect your frontend to the blockchain using JSON RPC blockchain providers, and the [RainbowKit], [wagmi], and [viem] stack.
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-- Compare and contrast public providers vs. vendor providers vs. wallet providers
-- Select the appropriate provider for several use cases
-- Set up a provider in wagmi and use it to connect a wallet
-- Protect API keys that will be exposed to the front end
-
-## Prerequisites
-
-### 1. Be familiar with modern, frontend web development
-
-In this tutorial, we'll be working with a React frontend built with [Next.js]. While you don't need to be an expert, we'll assume that you're comfortable with the basics.
-
-### 2. Possess a general understanding of the EVM and smart contract development
-
-This tutorial assumes that you're reasonably comfortable writing basic smart contracts. If you're just getting started, jump over to our [Base Learn] guides and start learning!
-
-## Types of Providers
-
-Onchain apps need frontends, sometimes called dApps, to enable your users to interact with your smart contracts. A _provider_ makes the connection from frontend to the blockchain, and is used to read data and send transactions.
-
-In blockchain development, the term _provider_ describes a company or service that provides an API enabling access to the blockchain as a service. This is distinct from the providers you wrap your app in using the [React Context API], though you'll use one of those to pass your blockchain provider deeply into your app.
-
-These services enable interacting with smart contracts without the developer needing to run and maintain their own blockchain node. Running a node is expensive, complicated, and challenging. In most cases, you'll want to start out with a provider. Once you start to get traction, you can evaluate the need to [run your own node], or switch to a more advanced architecture solution, such as utilizing [Subgraph].
-
-Figuring out which type of provider to use can be a little confusing at first. As with everything blockchain, the landscape changes rapidly, and search results often return out-of-date information.
-
-
-New onchain devs sometimes get the impression that there are free options for connecting their apps to the blockchain. Unfortunately, this is not really true. Blockchain data is still 1's and 0's, fetched by computation and served to the internet via servers.
-
-It costs money to run these, and you will eventually need to pay for the service.
-
-
-You'll encounter providers divided into three general categories: Public Providers, Wallet Providers, and Vendor Providers
-
-### Public Providers
-
-Many tutorials and guides, including the getting started guide for [wagmi], use a _Public Provider_ as the default to get you up and running. Public means that they're open, permissionless, and free, so the guides will also usually warn you that you need to add another provider if you don't want to run into rate limiting. Listen to these warnings! The rate-limits of public providers are severe, and you'll start getting limited very quickly.
-
-In wagmi, a public client is automatically included in the default config. This client is just a wrapper setting up a [JSON RPC] provider using the `chain` and `rpcUrls` listed in Viem's directory of chain information. You can view the [data for Base Sepolia here].
-
-Most chains will list this information in their docs as well. For example, on the network information pages for [Base] and [Optimism]. If you wanted, you could manually set up a `jsonRpcProvider` in wagmi using this information.
-
-### Wallet Providers
-
-Many wallets, including Coinbase Wallet and MetaMask, inject an Ethereum provider into the browser, as defined in [EIP-1193]. The injected provider is accessible via `window.ethereum`.
-
-Under the hood, these are also just JSON RPC providers. Similar to public providers, they are rate-limited.
-
-Older tutorials for early libraries tended to suggest using this method for getting started, so you'll probably encounter references to it. However, it's fallen out of favor, and you'll want to use the public provider for your initial connection experiments.
-
-### Vendor Providers
-
-A growing number of vendors provide access to blockchain nodes as a service. Visiting the landing pages for [QuickNode], [Alchemy], or [Coinbase Developer Platform (CDP)] can be a little confusing. Each of these vendors provides a wide variety of services, SDKs, and information.
-
-Luckily, you can skip most of this if you're just trying to get your frontend connected to your smart contracts. You'll just need to sign up for an account, and get an endpoint, or a key, and configure your app to connect to the provider(s) you choose.
-
-It is worth digging in to get a better understanding of how these providers charge you for their services. The table below summarizes some of the more important API methods, and how you are charged for them by each of the above providers.
-
-Note that the information below may change, and varies by network. Each provider also has different incentives, discounts, and fees for each level of product. They also have different allowances for calls per second, protocols, and number of endpoints. Please check the source to confirm!
-
-| | [Alchemy Costs] | [QuickNode Costs] | [CDP Costs] |
-| :-------------- | :--------------- | :---------------- | :----------------- |
-| Free Tier / Mo. | 3M compute units | 50M credits | 500M billing units |
-| Mid Tier / Mo. | 1.5B CUs @ $199 | 3B credits @ $299 | Coming soon |
-| eth_blocknumber | 10 | 20 | 30 |
-| eth_call | 26 | 20 | 30 |
-| eth_getlogs | 75 | 20 | 100 |
-| eth_getbalance | 19 | 20 | 30 |
-
-To give you an idea of usage amounts, a single wagmi `useContractRead` hook set to watch for changes on a single `view` via a TanStack query and `useBlockNumber` will call `eth_blocknumber` and `eth_call` one time each, every 4 seconds.
-
-## Connecting to the Blockchain
-
-[RainbowKit] is a popular library that works with [wagmi] to make it easy to connect, disconnect, and change between multiple wallets. It's batteries-included out of the box, and allows for a great deal of customization of the list of wallets and connect/disconnect button.
-
-You'll be using RainbowKit's [quick start] to scaffold a new project for this tutorial. Note that at the time of writing, it does **not** use the Next.js app router. See [Building an Onchain App] if you wish to set this up instead.
-
-
-The script doesn't allow you to use `.` to create a project in the root of the folder you run it from, so you'll want to run it from your `src` directory, or wherever you keep your project folders.
-
-It will create a folder with the project name you give, and create the files inside.
-
-
-Open up a terminal and run:
-
-```bash
-yarn create @rainbow-me/rainbowkit
-```
-
-Give your project a name, and wait for the script to build it. It will take a minute or two.
-
-
-If you get an error because you are on the wrong version of node, change to the correct version then **delete everything** and run the script again.
-
-
-### Scaffolded App
-
-Open your new project in the editor of your choice, and open `pages/_app.tsx`. Here, you'll find a familiar Next.js app wrapped in [context providers] for the TanStack QueryProvider, RainbowKit, and wagmi.
-
-```tsx
-function MyApp({ Component, pageProps }: AppProps) {
- return (
-
-
-
-
-
-
-
- );
-}
-```
-
-Note that these providers are using React's context feature to pass the blockchain providers and configuration into your app. It can be confusing to have the word _provider_ meaning two different things in the same file, or even the same line of code!
-
-Before you can do anything else, you need to obtain a _WalletConnect_ `projectId`.
-
-Open up the [WalletConnect] homepage, and create an account, and/or sign in using the method of your choice.
-
-Click the `Create` button in the upper right of the `Projects` tab.
-
-
-
-
-
-Enter a name for your project, select the `App` option, and click `Create`.
-
-
-
-
-
-Copy the _Project ID_ from the project information page, and paste it in as the `projectId` in `getDefaultWallets`.
-
-```tsx
-const { connectors } = getDefaultWallets({
- appName: 'RainbowKit App',
- projectId: 'YOUR_PROJECT_ID',
- chains,
-});
-```
-
-
-Remember, anything you put on the frontend is public! That includes this id, even if you use environment variables to better manage this type of data. Next.js reminds you of the risk, by requiring you to prepend `NEXT_PUBLIC_` to any environment variables that can be read by the browser.
-
-Before you deploy, make sure you configure the rest of the items in the control panel to ensure only your site can use this id.
-
-
-### Public Provider
-
-By default, the setup script will configure your app to use the built-in public provider, and connect to a number of popular chains. To simply matters, remove all but `mainnet` and `base`.
-
-```tsx
-const config = getDefaultConfig({
- appName: 'RainbowKit App',
- projectId: 'YOUR_APP_ID_HERE',
- chains: [mainnet, base],
- ssr: true,
-});
-```
-
-Open the terminal and start the app with:
-
-```bash
-yarn run dev
-```
-
-Click the `Connect Wallet` button, select your wallet from the modal, approve the connection, and you should see your network, token balance, and address or ENS name at the top of the screen. Select your wallet from the modal.
-
-
-
-
-
-You've connected with the Public Provider!
-
-
-
-
-
-### QuickNode
-
-To select your provider(s), you'll use [`createConfig`] instead of `getDefaultConfig`. The [`transports`] property allows you to configure how you wish to connect with multiple networks. If you need more than one connector for a given network, you can use [`fallbacks`].
-
-First, set up using [QuickNode] as your provider. Replace your import of the default config from RainbowKit with `createConfig` and `http` from wagmi:
-
-```tsx
-import { createConfig, http, WagmiProvider } from 'wagmi';
-// ...Chains import
-import { RainbowKitProvider } from '@rainbow-me/rainbowkit';
-```
-
-You'll need an RPC URL, so open up [QuickNode]'s site and sign up for an account if you need to. The free tier will be adequate for now, you may need to scroll down to see it. Once you're in, click `Endpoints` on the left side, then click `+ Create Endpoint`.
-
-On the next screen, you'll be asked to select a chain. Each endpoint only works for one. Select `Base`, click `Continue`.
-
-
-
-
-
-For now, pick `Base Mainnet`, but you'll probably want to delete this endpoint and create a new one for Sepolia when you start building. The free tier only allows you to have one at a time.
-
-If you haven't already picked a tier, you'll be asked to do so, then you'll be taken to the endpoints page, which will display your endpoints for HTTP and WSS.
-
-
-As with your WalletConnect Id, these endpoints will be visible on the frontend. Be sure to configure the allowlist!
-
-
-Use this endpoint to add an `http` `transport` to your config:
-
-```tsx
-const config = createConfig({
- chains: [mainnet, base],
- ssr: true,
- transports: {
- [base.id]: http('YOUR PROJECT URL'),
- [mainnet.id]: http('TODO'),
- },
-});
-```
-
-Now, the app will use your QuickNode endpoint for the Base network. Note that you don't need an app name or WalletConnect Id, because you are no longer using WalletConnect.
-
-To test this out, switch networks a few times. You'll know it's working if you see your balance when Base is the selected network. You haven't added mainnet, so you'll get an error in the console and no balance when you switch to that.
-
-### Alchemy
-
-[Alchemy] is [no longer baked into wagmi], but it still works the same as any other RPC provider. As with QuickNode, you'll need an account and a key. Create an account and/or sign in, navigate to the `Apps` section in the left sidebar, and click `Create new app`.
-
-
-
-
-
-Select Base Mainnet, and give your app a name.
-
-
-Once again, remember to configure the [allowlist] when you publish your app, as you'll be exposing your key to the world!
-
diff --git a/docs/learn/onchain-app-development/frontend-setup/overview.mdx b/docs/learn/onchain-app-development/frontend-setup/overview.mdx
deleted file mode 100644
index af693f4b9..000000000
--- a/docs/learn/onchain-app-development/frontend-setup/overview.mdx
+++ /dev/null
@@ -1,53 +0,0 @@
----
-title: 'Overview'
-description: An overview of this course.
-hide_table_of_contents: false
----
-
-# Overview
-
-Welcome! The course you are about to begin will rapidly introduce you to frontend web development for onchain apps and enable you to write websites that can call your smart contract functions in a similar way to how traditional sites interact with APIs.
-
-## Prerequisites
-
-Before these lessons, you should:
-
-- Be comfortable with traditional frontend development using React, ideally with NextJS
-- Possess a general understanding of the EVM and smart contracts
-
----
-
-## Objectives
-
-By the end of this course, you should be able to:
-
-- **Frontend Setup**
- - Identify the role of a wallet aggregator in an onchain app
- - Debate the pros and cons of using a template
- - Scaffold a new onchain app with RainbowKit
- - Add a wallet connection to a standard template app
-- **Connecting to the Blockchain**
- - Compare and contrast public providers vs. vendor providers vs. wallet providers
- - Select the appropriate provider for several use cases
- - Set up a provider in wagmi and use it to connect a wallet
- - Protect API keys that will be exposed to the front end
-- **Reading and Displaying Data**
- - Implement the `useAccount` hook to show the user's address, connection state, network, and balance
- - Implement an `isMounted` hook to prevent hydration errors
- - Implement wagmi's `useReadContract` hook to fetch data from a smart contract
- - Convert data fetched from a smart contract to information displayed to the user
- - Identify the caveats of reading data from automatically-generated getters
- - Enable the `watch` feature of `useReadContract` to automatically fetch updates from the blockchain
- - Describe the costs of using the `watch` feature, and methods to reduce those costs
- - Configure arguments to be passed with a call to a `pure` or `view` smart contract function
- - Call an instance of `useReadContract` on demand
- - Utilize `isLoading` and `isFetching` to improve user experience
-- **Writing to Contracts**
- - Implement wagmi's `useWriteContract` hook to send transactions to a smart contract
- - Configure the options in `useWriteContract`
- - Display the execution, success, or failure of a function with button state changes, and data display
- - Implement Wagmi's `usePrepareContractWrite` and `useWriteContract` to send transactions to a smart contract
- - Configure the options in `useSimulateContract` and `useWriteContract`
- - Call a smart contract function on-demand using the write function from `useWriteContract`, with arguments and a value
-
----
diff --git a/docs/learn/onchain-app-development/frontend-setup/viem.mdx b/docs/learn/onchain-app-development/frontend-setup/viem.mdx
deleted file mode 100644
index 19a231df6..000000000
--- a/docs/learn/onchain-app-development/frontend-setup/viem.mdx
+++ /dev/null
@@ -1,107 +0,0 @@
----
-title: viem
-slug: /tools/viem
-description: Documentation for using Viem, a TypeScript interface for EVM-compatible blockchains. This page covers installation, setup, and various functionalities such as reading and writing blockchain data and interacting with smart contracts on Base.
----
-
-# viem
-
-
-Viem is currently only available on Base Sepolia testnet.
-
-
-[viem](https://viem.sh/) a TypeScript interface for Ethereum that provides low-level stateless primitives for interacting with Ethereum.
-
-You can use viem to interact with smart contracts deployed on Base.
-
-## Install
-
-To install viem run the following command:
-
-```bash
-npm install --save viem
-```
-
-## Setup
-
-Before you can start using viem, you need to setup a [Client](https://viem.sh/docs/clients/intro.html) with a desired [Transport](https://viem.sh/docs/clients/intro.html) and [Chain](https://viem.sh/docs/chains/introduction).
-
-```javascript
-import { createPublicClient, http } from 'viem';
-import { base } from 'viem/chains';
-
-const client = createPublicClient({
- chain: base,
- transport: http(),
-});
-```
-
-
-To use Base, you must specify `base` as the chain when creating a Client.
-
-To use Base Sepolia (testnet), replace `base` with `baseSepolia`.
-
-
-## Reading data from the blockchain
-
-Once you have created a client, you can use it to read and access data from Base using [Public Actions](https://viem.sh/docs/actions/public/introduction.html)
-
-Public Actions are client methods that map one-to-one with a "public" Ethereum RPC method (`eth_blockNumber`, `eth_getBalance`, etc.)
-
-For example, you can use the `getBlockNumber` client method to get the latest block:
-
-```javascript
-const blockNumber = await client.getBlockNumber();
-```
-
-## Writing data to the blockchain
-
-In order to write data to Base, you need to create a Wallet client (`createWalletClient`) and specify an [`Account`](https://ethereum.org/en/developers/docs/accounts/) to use.
-
-```javascript
-import { createWalletClient, custom } from 'viem'
-import { base } from 'viem/chains'
-
-//highlight-start
-const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })
-//highlight-end
-
-const client = createWalletClient({
- //highlight-next-line
- account,
- chain: base,
- transport: custom(window.ethereum)
-})
-
-client.sendTransaction({ ... })
-```
-
-
-In addition to making a JSON-RPC request (`eth_requestAccounts`) to get an Account, viem provides various helper methods for creating an `Account`, including: [`privateKeyToAccount`](https://viem.sh/docs/accounts/local/privateKeyToAccount), [`mnemonicToAccount`](https://viem.sh/docs/accounts/local/mnemonicToAccount), and [`hdKeyToAccount`](https://viem.sh/docs/accounts/local/hdKeyToAccount).
-
-To use Base Sepolia (testnet), replace `base` with `baseSepolia`.
-
-
-## Interacting with smart contracts
-
-You can use viem to interact with a smart contract on Base by creating a `Contract` instance using [`getContract`](https://viem.sh/docs/contract/getContract.html) and passing it the contract ABI, contract address, and [Public](https://viem.sh/docs/clients/public.html) and/or [Wallet](https://viem.sh/docs/clients/wallet.html) Client:
-
-```javascript
-import { getContract } from 'viem';
-import { wagmiAbi } from './abi';
-import { publicClient } from './client';
-
-// 1. Create contract instance
-const contract = getContract({
- address: 'CONTRACT_ADDRESS',
- abi: wagmiAbi,
- publicClient,
-});
-
-// 2. Call contract methods, listen to events, etc.
-const result = await contract.read.totalSupply();
-```
-
-
-`CONTRACT_ADDRESS` is the address of the deployed contract.
-
diff --git a/docs/learn/onchain-app-development/frontend-setup/wallet-connectors.mdx b/docs/learn/onchain-app-development/frontend-setup/wallet-connectors.mdx
deleted file mode 100644
index fa45223f2..000000000
--- a/docs/learn/onchain-app-development/frontend-setup/wallet-connectors.mdx
+++ /dev/null
@@ -1,94 +0,0 @@
----
-title: Wallet Connectors
-description: Learn about how wallet connector libraries aggregate wallets and make it easier to connect to them from your app.
-hide_table_of_contents: false
----
-
-# Wallet Connectors
-
-One of the most intimidating tasks when building an onchain app is making that initial connection between your users' wallets, and your app. Initial research often surfaces a bewildering number of wallets, each with their own SDKs, and own methods to manage the connection. Luckily, you don't actually need to manage all of this on your own. There are a number of wallet connector libraries specialized in creating a smooth and beautiful user experience to facilitate this connection.
-
-To further add to the confusion and difficulty, [Smart wallets] are growing in popularity. These advanced wallets allow users to create and manage wallets with [passkeys], and support, or will soon support, a growing array of features including session keys, account recovery, and more!
-
-[RainbowKit], the aggregator you'll use for this lesson, works with the Coinbase Smart Wallet out of the box, but you'll need to do a little bit of extra configuration to support users of both traditional wallets and smart wallets.
-
----
-
-## Objectives
-
-By the end of this guide you should be able to:
-
-- Identify the role of a wallet aggregator in an onchain app
-- Debate the pros and cons of using a template
-- Scaffold a new onchain app with RainbowKit
-- Support users of EOAs and the Coinbase Smart Wallet with the same app
-
----
-
-## Connecting to the Blockchain
-
-One of the many challenging tasks of building a frontend that can interface with your smart contracts is managing the user's connection between your onchain app and their [EOA] wallet. Not only is there an ever-growing suite of different wallets, but users can (and probably should!) use several different addresses within the same wallet app.
-
-[Rainbowkit] is one of several options that makes this a little bit easier by serving as an aggregator of wallets, and handling some of the details of connecting them. Alternatives include [ConnectKit], and [Dynamic], which are both excellent choices as well.
-
-Each of these include customizable UI/UX components for inviting the user to connect, displaying connection status, and selecting which wallet they wish to use.
-
-### Using the Quick Start
-
-If you're just trying to get up and running as quickly as possible, you can use RainbowKit's [quick start] script to scaffold an app from their template, with a single command. If you're using Yarn:
-
-```bash
-yarn create @rainbow-me/rainbowkit
-```
-
-
-The script doesn't accept `.` as a project name, so you'll want to run this script in your `src` directory, or wherever you keep your projects. It will create a folder with the same name as your project, and install the project files inside.
-
-
-
-Once it's done, simply run the app with:
-
-```bash
-yarn run dev
-```
-
-Using the script is fast, but it does mean less choice. In this case, it builds the app on top of [Next.js], which is great if you want to use it, but not helpful if you prefer to work from a different framework, such as [Create React App], or [Remix] (the React framework, not the Solidity IDE). The script also doesn't help you if you want to add an onchain integration to an existing site.
-
-
-The Rainbowkit template has been updated to wagmi 2.X, but it does **not** use the Next.js app router. You'll need to install it manually if you wish to use the latest patterns.
-
-The [Building an Onchain App] tutorial will show you how to do this!
-
-
-
-### Coinbase Smart Wallet
-
-If you have the Coinbase Wallet extension, you might be wondering where the smart wallet can be found. By default, the smart wallet will only be invoked if you click the `Coinbase Wallet` button to log in **and** you **don't** have the browser extension. To test, open a private window with extensions disabled and try to log in.
-
-Selecting `Rainbow`, `MetaMask`, or `WalletConnect` will display a QR code so that the user can log in with their phone. Picking `Coinbase Wallet` will instead invoke the smart wallet login.
-
-This flow can be improved upon, as new crypto users won't know that digging for the smart wallet is the best path forward, and existing users who are trying to migrate to the smart wallet don't have that option.
-
-See our tutorial on how to [Use the Coinbase Smart Wallet and EOAs with OnchainKit] for more details!
-
----
-
-## Conclusion
-
-In this article, you've learned how libraries such as [Rainbowkit], [ConnectKit], and [Dynamic], aggregate wallets and make it easier for you to connect your app to your users' wallet of choice. You've also learned how you can use a template to quickly create the foundation of your app. Finally, you've learned that the cost of using a template is that it does make some choices for you.
-
----
-
-[RainbowKit]: https://www.rainbowkit.com/
-[wagmi]: https://wagmi.sh/
-[wallet]: https://ethereum.org/en/developers/docs/accounts/
-[ConnectKit]: https://ethereum.org/en/developers/docs/accounts/
-[Dynamic]: https://www.dynamic.xyz/
-[quick start]: https://www.rainbowkit.com/docs/installation
-[Next.js]: https://nextjs.org/
-[Create React App]: https://create-react-app.dev/
-[Remix]: https://remix.run/
-[Building an Onchain App]: ./building-an-onchain-app
-[Smart wallets]: https://www.coinbase.com/wallet/smart-wallet
-[passkeys]: https://safety.google/authentication/passkey/
-[Use the Coinbase Smart Wallet and EOAs with OnchainKit]: https://docs.base.org/tutorials/smart-wallet-and-eoa-with-onchainkit
diff --git a/docs/learn/onchain-app-development/frontend-setup/web3.mdx b/docs/learn/onchain-app-development/frontend-setup/web3.mdx
deleted file mode 100644
index ff726add9..000000000
--- a/docs/learn/onchain-app-development/frontend-setup/web3.mdx
+++ /dev/null
@@ -1,108 +0,0 @@
----
-title: web3.js
-description: Documentation for using web3.js, a JavaScript library for interacting with EVM-compatible blockchains. This page covers installation, setup, connecting to the Base network and interacting with smart contracts.
----
-
-# web3.js
-
-[web3.js](https://web3js.org/) is a JavaScript library that allows developers to interact with EVM-compatible blockchain networks.
-
-You can use web3.js to interact with smart contracts deployed on the Base network.
-
-## Install
-
-To install web3.js run the following command:
-
-```bash
-npm install web3
-```
-
-## Setup
-
-Before you can start using web3.js, you need to import it into your project.
-
-Add the following line of code to the top of your file to import web3.js:
-
-```javascript
-//web3.js v1
-const Web3 = require('web3');
-
-//web3.js v4
-const { Web3 } = require('web3');
-```
-
-## Connecting to Base
-
-You can connect to Base by instantiating a new web3.js `Web3` object with a RPC URL of the Base network:
-
-```javascript
-const { Web3 } = require('web3');
-
-const web3 = new Web3('https://mainnet.base.org');
-```
-
-
-To alternatively connect to Base Sepolia (testnet), change the above URL from `https://mainnet.base.org` to `https://sepolia.base.org`.
-
-
-## Accessing data
-
-Once you have created a provider, you can use it to read data from the Base network.
-
-For example, you can use the `getBlockNumber` method to get the latest block:
-
-```javascript
-async function getLatestBlock(address) {
- const latestBlock = await web3.eth.getBlockNumber();
- console.log(latestBlock.toString());
-}
-```
-
-## Deploying contracts
-
-Before you can deploy a contract to the Base network using web3.js, you must first create an account.
-
-You can create an account by using `web3.eth.accounts`:
-
-```javascript
-const privateKey = "PRIVATE_KEY";
-const account = web3.eth.accounts.privateKeyToAccount(privateKey);
-```
-
-
-`PRIVATE_KEY` is the private key of the wallet to use when creating the account.
-
-
-## Interacting with smart contracts
-
-You can use web3.js to interact with a smart contract on Base by instantiating a `Contract` object using the ABI and address of a deployed contract:
-
-```javascript
-const abi = [
-... // ABI of deployed contract
-];
-
-const contractAddress = "CONTRACT_ADDRESS"
-
-const contract = new web3.eth.Contract(abi, contractAddress);
-```
-
-Once you have created a `Contract` object, you can use it to call desired methods on the smart contract:
-
-```javascript
-async function setValue(value) {
- // write query
- const tx = await contract.methods.set(value).send();
- console.log(tx.transactionHash);
-}
-
-async function getValue() {
- // read query
- const value = await contract.methods.get().call();
- console.log(value.toString());
-}
-```
-
-
-For more information on deploying contracts on Base, see [Deploying a Smart Contract](../smart-contract-development/hardhat/deploy-with-hardhat.md).
-
diff --git a/docs/learn/onchain-app-development/reading-and-displaying-data/configuring-useReadContract.mdx b/docs/learn/onchain-app-development/reading-and-displaying-data/configuring-useReadContract.mdx
deleted file mode 100644
index 4a0b2bd5c..000000000
--- a/docs/learn/onchain-app-development/reading-and-displaying-data/configuring-useReadContract.mdx
+++ /dev/null
@@ -1,212 +0,0 @@
----
-title: Configuring `useReadContract`
-description: Configure the properties of the `useReadContract` hook.
-hide_table_of_contents: false
----
-
-# Configuring `useReadContract`
-
-The [`useReadContract`] hook has a number of configurable properties that will allow you to adapt it to your needs. You can combine the functionality of TanStack queries with [`useBlockNumber`] to watch the blockchain for changes, although doing so will consume a number of API calls.
-
----
-
-## Objectives
-
-By the end of this guide you should be able to:
-
-- Use `useBlockNumber` and the `queryClient` to automatically fetch updates from the blockchain
-- Describe the costs of using the above, and methods to reduce those costs
-- Configure arguments to be passed with a call to a `pure` or `view` smart contract function
-- Call an instance of `useReadContract` on demand
-- Utilize `isLoading` and `isFetching` to improve user experience
-
----
-
-## Fetching Updates from the Blockchain
-
-You'll continue with the project you've been building and last updated while learning about the [`useReadContract` hook].
-
-Once the excitement of your accomplishment of finally reading from your own contract subsides, try using BaseScan to add another issue, or vote on an existing issue. You'll notice that your frontend does **not** update. There are a few ways to handle this.
-
-
-
-Use `useBlockNumber` with the `watch` feature to automatically keep track of the block number, then use the `queryClient` to update when that changes. **Make sure** you decompose the `queryKey` from the return of `useReadContract`.
-
-```tsx
-import { useEffect, useState } from 'react';
-import { useReadContract, useBlockNumber } from 'wagmi';
-import { useQueryClient } from '@tanstack/react-query';
-
-// Other Code
-
-export function IssueList() {
- // Other Code
-
- const queryClient = useQueryClient();
- const { data: blockNumber } = useBlockNumber({ watch: true });
-
- const {
- data: issuesData,
- isError: issuesIsError,
- isPending: issuesIsPending,
- queryKey: issuesQueryKey,
- } = useReadContract({
- address: contractData.address as `0x${string}`,
- abi: contractData.abi,
- functionName: 'getAllIssues',
- });
-
- useEffect(() => {
- queryClient.invalidateQueries({ queryKey: issuesQueryKey });
- }, [blockNumber, queryClient, issuesQueryKey]);
-
- // Return code
-}
-```
-
-Don't do this, either use multi-call via [`useReadContracts`], or consolidate your `view`s into a single function that fetches all the data you need in one call.
-
-
-
-Stop watching the blockchain if the website doesn't have focus. Add a state variable to count how many times the function has settled, and one for if the page is focused. Set up event listeners to set the state of the latter when the page is focused or blurred.
-
-```tsx
-const [timesCalled, setTimesCalled] = useState(0);
-const [pageIsFocused, setPageIsFocused] = useState(true);
-
-useEffect(() => {
- const onFocus = () => setPageIsFocused(true);
- const onBlur = () => setPageIsFocused(false);
-
- window.addEventListener('focus', onFocus);
- window.addEventListener('blur', onBlur);
-
- return () => {
- window.removeEventListener('focus', onFocus);
- window.removeEventListener('blur', onBlur);
- };
-}, []);
-```
-
-Update the `watch` for `useBlockNumber` so that it only does so if `pageIsFocused`.
-
-```tsx
-const { data: blockNumber } = useBlockNumber({ watch: pageIsFocused });
-```
-
-Add a line to the `useEffect` for `blockNumber` and increment your counter as well.
-
-```tsx
-useEffect(() => {
- setTimesCalled((prev) => prev + 1);
- queryClient.invalidateQueries({ queryKey: issuesQueryKey });
-}, [blockNumber, queryClient]);
-```
-
-Finally, surface your counter in the component.
-
-```tsx
-return (
-
-
Number of times called
-
{timesCalled.toString()}
-
{'Has focus: ' + pageIsFocused}
-
All Issues
-
{renderIssues()}
-
-);
-```
-
-
-Adjust your [`pollingInterval`] by setting it in `getDefaultConfig` in `_app.tsx`:
-
-```tsx
-const config = getDefaultConfig({
- appName: 'RainbowKit App',
- projectId: 'YOUR_PROJECT_ID',
- chains: [baseSepolia],
- ssr: true,
- pollingInterval: 30_000,
-});
-```
-
-Setting it to 30 seconds, or 30,000 milliseconds, will reduce your API calls dramatically, without negatively impacting members of the DAO.
-
-
-Use a similar system to call your update function on demand. Add a button, a handler for that button, and a state variable for it to set:
-
-```tsx
-const [triggerRead, setTriggerRead] = useState(false);
-
-const handleTriggerRead = () => {
- setTriggerRead(true);
-};
-```
-
-```tsx
-return (
-
-
-
Number of times called
-
{timesCalled.toString()}
-
{'Has focus: ' + pageIsFocused}
-
All Issues
-
{renderIssues()}
-
-);
-```
-
-Finally, set `watch` to equal `triggerRead`, instead of `pageIsFocused`, and reset `triggerRead` in the `useEffect`.
-
-```tsx
-const { data: blockNumber } = useBlockNumber({ watch: triggerRead });
-
-// Other code...
-
-useEffect(() => {
- setTriggerRead(false);
- queryClient.invalidateQueries({ queryKey: issuesQueryKey });
-}, [blockNumber, queryClient]);
-```
-
-
-Use the "is" return values to set UI elements depending on the status of the hook as it attempts to call a function on the blockchain.
-
-```tsx
-
-```
-You'll probably see the button flicker very quickly since the call doesn't take very long. For a production app, you'd need to add additional handling to smooth out the experience.
-
-
-Arguments are passed into a `useReadContract` hook by adding an array of arguments, in order, to the `args` property. Common practice is to use React state variables set by UI elements to enable the arguments to be set and modified. For example, you might create a drop-down to set `issueNumber`, then fetch that issue with:
-
-```tsx
-const [issueNumber, setIssueNumber] = useState(0);
-
-const { isLoading: getIssueIsLoading } = useReadContract({
- address: contractData.address as `0x${string}`,
- abi: contractData.abi,
- functionName: 'getIssue',
- args: [issueNumber],
-});
-```
-
-
-
----
-
-## Conclusion
-
-In this guide, you've learned how to use the `watch` feature of `useBlockNumber` combined with `useEffect` and `queryClient.invalidateQueries` to enable your frontend to see updates to your smart contract. You've also learned the costs of doing so, and some strategies for mitigation. You've learned how to pass arguments to your functions. Finally, you've learned how to use the properties returned by `useReadContract` to adjust your UI to improve the experience for your users.
-
----
-
-[wagmi]: https://wagmi.sh/
-[`useReadContract`]: https://wagmi.sh/react/hooks/useReadContract
-[`useReadContract` hook]: ./useReadContract
-[`useBlockNumber`]: https://wagmi.sh/react/api/hooks/useBlockNumber
-[removed]: https://wagmi.sh/react/guides/migrate-from-v1-to-v2#removed-watch-property
-[`useReadContracts`]: https://wagmi.sh/react/hooks/useReadContracts
-[`pollingInterval`]: https://wagmi.sh/react/api/createConfig#pollinginterval
diff --git a/docs/learn/onchain-app-development/reading-and-displaying-data/useAccount.mdx b/docs/learn/onchain-app-development/reading-and-displaying-data/useAccount.mdx
deleted file mode 100644
index 52d09e3b2..000000000
--- a/docs/learn/onchain-app-development/reading-and-displaying-data/useAccount.mdx
+++ /dev/null
@@ -1,226 +0,0 @@
----
-title: The `useAccount` Hook
-description: Learn how to access information about the connected user's wallet.
-hide_table_of_contents: false
----
-
-# The `useAccount` Hook
-
-[wagmi] is a library that provides React hooks that trade a somewhat complex setup process for a great developer experience when building a frontend around the constraints and quirks of onchain building. One of the hooks, `useAccount`, provides access to information about your users' wallet and connection information.
-
-You can use this for connection-status-based rendering, to enable or disable controls or views based on address, and many other useful tasks.
-
----
-
-## Objectives
-
-By the end of this guide you should be able to:
-
-- Implement the `useAccount` hook to show the user's address, connection state, network, and balance
-- Implement an `isMounted` hook to prevent hydration errors
-
----
-
-## Displaying Connection Information
-
-We'll be working from an app generated by RainbowKit's [quick start]. Either open the one you created when we were exploring [Wallet Connectors], or create a new one for this project.
-
-Either way, change the list of chains to only include `baseSepolia` as the network option. You don't want to accidentally spend real money while developing!
-
-You can set up your providers as described in [Introduction to Providers], or use the default from RainbowKit:
-
-```tsx
-const config = getDefaultConfig({
- appName: 'RainbowKit App',
- projectId: 'YOUR APP ID',
- chains: [baseSepolia],
- ssr: true,
-});
-```
-
-Either way, be sure to set `ssr` to `true`, or you will get a [hydration error] from Next.js.
-
-### The `useAccount` Hook
-
-The [`useAccount`] hook allows you to access account and connection data from within any of your components.
-
-Add a folder for `components` and a file called `ConnectionWindow.tsx` in that folder. Add the below component to the file, and replace the boilerplate text in `index.tsx` with an instance of it.
-
-```tsx
-// ConnectionWindow.tsx
-export function ConnectionWindow() {
- return (
-
-
Connection Status
-
- );
-}
-```
-
-```tsx
-// index.tsx
-import { ConnectButton } from '@rainbow-me/rainbowkit';
-import type { NextPage } from 'next';
-import Head from 'next/head';
-import styles from '../styles/Home.module.css';
-import { ConnectionWindow } from '../components/ConnectionWindow';
-
-const Home: NextPage = () => {
- return (
-
-
-
-
-
-
- );
-};
-
-export default Home;
-```
-
-For the purposes of this exercise, open `styles/Home.module.css` and **delete or comment out** `.main`. Doing so will move the content to the top of the page, which will prevent the RainbowKit modal from blocking your ability to see changes.
-
-Return to `ConnectionWindow.tsx` and add the `useAccount` hook to the top, where you'd add any state variables. The general pattern for wagmi hooks is you decompose the properties you want to use from a function call of the name of the hook. For some, you'll add a config object to that call, but it's not needed for this one.
-
-```tsx
-import { useAccount } from 'wagmi';
-
-export function ConnectionWindow() {
- const { address, isConnected, isConnecting, isDisconnected } = useAccount();
-
- return (
-
-
Connection Status
-
- );
-}
-```
-
-You can see all the deconstructable return options in the [UseAccountReturnType]:
-
-Update your `
` to show the address of the connected wallet:
-
-```tsx
-
-
Connection Status
-
-
{'Address: ' + address}
-
-
-```
-
-Test it out by connecting and disconnecting with your wallet. You should see your full address when you are connected, and the address will be `undefined` when you are disconnected.
-
-### Connection Status Conditional Rendering
-
-It isn't very nice to display a value of `undefined` to the user, so let's use the connection status values for conditional rendering depending on whether the user is disconnected, connected, or connecting.
-
-A common pattern is to use the conditional directly in the html return of a component or render function. For example, we could add a line to show that we're connecting as demonstrated:
-
-```
-
-
Connection Information
-
- {!isConnecting &&
Please click Connect in your wallet...
}
-
{"Address: " + address}
-
-
-```
-
-Connect and disconnect your wallet a few times. The `isConnecting` state is true while the _Connect to website_ wallet UI is open.
-
-Autoconnect is enabled by default, so you'll need to clear the connection from your wallet settings to see this more than once. Otherwise, it will briefly flash as the auto-connect processes.
-
-Use the `connected` property in the same way to only render the wallet address if there is a wallet connected. Similarly, use the `isDisconnected` property to show a message asking the user to connect.
-
-```
-
-
Connection Information
-
- {isConnecting &&
Please click Connect in your wallet...
}
- {isConnected &&
{"Address: " + address}
}
- {isDisconnected &&
Please connect your wallet to use this app.
}
-
-
-```
-
----
-
-## Conclusion
-
-In this guide, you've learned how the `useAccount` hook gives you access to information about the user's connection status and wallet. It can be used in any part of your app that is wrapped by the wagmi context provider. You've also learned a technique for conditional rendering based on connection status. Finally, you've learned to set the `ssr` flag to prevent hydration errors due to the client and server possessing different information about the user's connection status.
-
----
-
-[RainbowKit]: https://www.rainbowkit.com/
-[wagmi]: https://wagmi.sh/
-[quick start]: https://www.rainbowkit.com/docs/installation/
-[Wallet Connectors]: ../frontend-setup/wallet-connectors/
-[`useAccount`]: https://wagmi.sh/react/hooks/useAccount
-[hydration error]: https://nextjs.org/docs/messages/react-hydration-error
-[Introduction to Providers]: https://docs.base.org/cookbook/client-side-development/introduction-to-providers
-[UseAccountReturnType]: https://wagmi.sh/react/api/hooks/useAccount#return-type
-
-
-
-Change the list of chains to only include `baseSepolia` as the network option. You can set up your providers as described in [Introduction to Providers], or use the default from RainbowKit:
-
-```tsx
-const config = getDefaultConfig({
- appName: 'RainbowKit App',
- projectId: 'YOUR APP ID',
- chains: [baseSepolia],
- ssr: true,
-});
-```
-
-Be sure to set `ssr` to `true`, or you will get a [hydration error] from Next.js.
-
-
-
-Add the [`useAccount`] hook to the top of your component, where you'd add any state variables. The general pattern for wagmi hooks is you decompose the properties you want to use from a function call of the name of the hook. For some, you'll add a config object to that call, but it's not needed for this one.
-
-```tsx
-import { useAccount } from 'wagmi';
-
-export function ConnectionWindow() {
- const { address, isConnected, isConnecting, isDisconnected } = useAccount();
-
- return (
-
-
Connection Status
-
- );
-}
-```
-
-
-Update your `
` to show the address of the connected wallet:
-
-```tsx
-
-
Connection Status
-
-
{'Address: ' + address}
-
-
-```
-Test it out by connecting and disconnecting with your wallet. You should see your full address when you are connected, and the address will be `undefined` when you are disconnected.
-
-
-Use the connection status values for conditional rendering depending on whether the user is disconnected, connected, or connecting.
-
-```tsx
-
-
Connection Information
-
- {isConnecting &&
Please click Connect in your wallet...
}
- {isConnected &&
{"Address: " + address}
}
- {isDisconnected &&
Please connect your wallet to use this app.
}
-
-
-```
-
-
-
diff --git a/docs/learn/onchain-app-development/reading-and-displaying-data/useReadContract.mdx b/docs/learn/onchain-app-development/reading-and-displaying-data/useReadContract.mdx
deleted file mode 100644
index 52e36ed11..000000000
--- a/docs/learn/onchain-app-development/reading-and-displaying-data/useReadContract.mdx
+++ /dev/null
@@ -1,463 +0,0 @@
----
-title: The `useReadContract` Hook
-description: Learn how to call view and pure functions from a smart contract.
-hide_table_of_contents: false
----
-
-# The `useReadContract` Hook
-
-The `useReadContract` hook is [wagmi]'s method of calling `pure` and `view` functions from your smart contracts. As with `useAccount`, `useReadContract` contains a number of helpful properties to enable you to manage displaying information to your users.
-
----
-
-## Objectives
-
-By the end of this guide you should be able to:
-
-- Implement wagmi's `useReadContract` hook to fetch data from a smart contract
-- Convert data fetched from a smart contract to information displayed to the user
-- Identify the caveats of reading data from automatically-generated getters
-
----
-
-## Contract Setup
-
-For this guide, you'll be continuing from the project you started for the [`useAccount` hook]. You'll work with an upgrade to the contract that you may have created if you completed the [ERC 20 Tokens Exercise]. See below for an example you can use if you don't already have your own!
-
-The contract creates a very simple DAO, in which users can create issues and vote for them, against them, or abstain. Anyone can `claim` 100 tokens. This is an insecure system for demonstration purposes, since it would be trivial to claim a large number of tokens with multiple wallets, then transfer them to a single address and use that to dominate voting.
-
-But it makes it much easier to test!
-
-
-If you're using your own contract, please redeploy it with the following `view` functions:
-
-```solidity
-function numberOfIssues() public view returns(uint) {
- return issues.length;
-}
-
-function getAllIssues() public view returns(ReturnableIssue[] memory) {
- ReturnableIssue[] memory allIssues = new ReturnableIssue[](issues.length);
-
- for(uint i = 0; i < issues.length; i++) {
- allIssues[i] = getIssue(i);
- }
-
- return allIssues;
-}
-```
-
-**You also need to make the `getIssue` function `public`. The original spec called for it to be `external`.**
-
-
-### Create Demo Issues
-
-To start, you'll need to put some data into your contract so that you can read it from your frontend. Open [Sepolia BaseScan], find your contract, connect with your wallet, and call the `claim` function.
-
-Add the following two issues:
-
-```text
-_issueDesc: We should enable light mode by default.
-_quorum: 2
-```
-
-```text
-_issueDesc: We should make inverted mouse controls the default selection.
-_quorum: 2
-```
-
-Switch to a **different wallet address**. Claim your tokens with the new address, and add one more issue:
-
-```text
-_issueDesc: Two spaces, not four, not tabs!
-_quorum: 2
-```
-
-Call the `getAllIssues` function under the `Read Contract` tab to make sure all three are there.
-
----
-
-## Reading from your Smart Contract
-
-To be able to read from your deployed smart contract, you'll need two pieces of information: the address and [ABI]. These are used as parameters in the `useReadContract` hook.
-
-If you're using [Hardhat], both of these can be conveniently found in a json file in the `deployments/` folder, named after your contract. For example, our contract is called `FEWeightedVoting`, so the file is `deployments/base-sepolia/FEWeightedVoting.json`.
-
-If you're using something else, it should produce a similar artifact, or separate artifacts with the [ABI] and address. If this is the case, make the adjustments you need when you import this data.
-
-Either way, add a folder called `deployments` and place a copy of the artifact file(s) inside.
-
-### Using the `useReadContract` Hook
-
-Add a file for a new component called `IssueList.tsx`. You can start with:
-
-```tsx
-import { useReadContract } from 'wagmi';
-
-export function IssueList() {
- return (
-
-
All Issues
-
{/* TODO: List each issue */}
-
- );
-}
-```
-
-You'll need to do some prepwork to enable Typescript to more easily interpret the data returned from your contract. Add an `interface` called `Issue` that matches with the `ReturnableIssue` type:
-
-```tsx
-interface Issue {
- voters: string[];
- issueDesc: string;
- votesFor: bigint;
- votesAgainst: bigint;
- votesAbstain: bigint;
- totalVotes: bigint;
- quorum: bigint;
- passed: boolean;
- closed: boolean;
-}
-```
-
-
-Be very careful here! `bigint` is the name of the type, `BigInt` is the name of the constructor for that type. If you incorrectly use the constructor as the type, much of your code will still work, but other parts will express very confusing bugs.
-
-
-Now, import `useState` and add a state variable to hold your list of `Issue`s.
-
-```tsx
-const [issues, setIssues] = useState([]);
-```
-
-You'll also need to import your contract artifact:
-
-```tsx
-import contractData from '../deployments/FEWeightedVoting.json';
-```
-
-Finally, the moment you've been waiting for: Time to read from your contract! Add an instance of the [`useReadContract`] hook. It works similarly to the [`useAccount`] hook. Configure it with:
-
-```tsx
-const {
- data: issuesData,
- isError: issuesIsError,
- isPending: issuesIsPending,
-} = useReadContract({
- address: contractData.address as `0x${string}`,
- abi: contractData.abi,
- functionName: 'getAllIssues',
-});
-```
-
-You can use `useEffect` to do something when the call completes and the data. For now, just log it to the console:
-
-```tsx
-useEffect(() => {
- if (issuesData) {
- const issuesList = issuesData as Issue[];
- console.log('issuesList', issuesList);
- setIssues(issuesList);
- }
-}, [issuesData]);
-```
-
-Add in instance of your new component to `index.tsx`:
-
-```tsx
-
-
-
-
-
-```
-
-Run your app, and you should see your list of issues fetched from the blockchain and displayed in the console!
-
-
-
-
-
-Breaking down the hook, you've:
-
-- Renamed the properties decomposed from `useReadContract` to be specific for our function. Doing so is helpful if you're going to read from more than one function in a file
-- Configured the hook with the address and ABI for your contract
-- Made use of `useEffect` to wait for the data to be returned from the blockchain, log it to the console, and set the list of `Issue`s in state.
-
-### Displaying the Data
-
-Now that you've got the data in state, you can display it via your component. One strategy to display a list of items is to compile a `ReactNode` array in a render function.
-
-```tsx
-function renderIssues() {
- return issues.map((issue) => (
-
- ));
-}
-```
-
-Then, call the render function in the return for your component:
-
-```tsx
-return (
-
-
All Issues
-
{renderIssues()}
-
-);
-```
-
-You'll now see your list of issues rendered in the browser! Congrats, you've finally made a meaningful connection between your smart contract and your frontend!
-
-### A Caveat with Automatic Getters
-
-Remember how the Solidity compiler creates automatic getters for all of your public state variables? This feature is very helpful, but it can create bewildering results when you use it for `struct`s that contain `mapping`s. Remember, nesting mappings **cannot** be returned outside the blockchain. The `enumerableSet` protects you from this problem, because it has private variables inside it, which prevents setting `issues` as `public`. Had we instead used a mapping, we'd lose this protection:
-
-```solidity
- // Code for demo only
- struct Issue {
- mapping(address => bool) voters;
- string issueDesc;
- uint votesFor;
- uint votesAgainst;
- uint votesAbstain;
- uint totalVotes;
- uint quorum;
- bool passed;
- bool closed;
- }
-```
-
-Redeploy with the above change, and add a second `useReadContract` to fetch an individual issue using the getter:
-
-```tsx
-// Bad code for example, do not use
-const {
- data: getOneData,
- isError: getOneIsError,
- isPending: getOneIsPending,
-} = useReadContract({
- address: contractData.address as `0x${string}`,
- abi: contractData.abi,
- functionName: 'issues',
- args: [1],
-});
-
-useEffect(() => {
- if (getOneData) {
- console.log('getOneData', getOneData);
- const issueOne = getOneData as Issue;
- console.log('Issue One', issueOne);
- }
-}, [getOneData]);
-```
-
-Everything appears to be working just fine, but how is `issueOne.desc` undefined? You can see it right there in the log!
-
-
-
-
-
-If you look closely, you'll see that `voters` is missing from the data in the logs. What's happening is that because the nested `mapping` cannot be returned outside the blockchain, it simply isn't. TypeScript then gets the `data` and does the best it can to cast it `as` an `Issue`. Since `voters` is missing, this will fail and it instead does the JavaScript trick of simply tacking on the extra properties onto the object.
-
-Take a look at the working example above where you retrieved the list. Notice that the keys in your `Issue` type are in that log, but are missing here. The assignment has failed, and all of the `Issue` properties are `null`.
-
----
-
-## Conclusion
-
-In this guide, you've learned how to use the `useReadContract` hook to call `pure` and `view` functions from your smart contracts. You then converted this data into React state and displayed it to the user. Finally, you explored a tricky edge case in which the presence of a nested `mapping` can cause a confusing bug when using the automatically-generated getter.
-
----
-
-## Simple DAO Contract Example
-
-Use this contract if you don't have your own from the [ERC 20 Tokens Exercise]. You can also use this if you want to cheat to get that badge. Doing so would be silly though!
-
-
-If you use your own contract, redeploy it with the `numberOfIssues` and `getAllIssues` functions from the bottom of the contract below. We'll need this for our first pass solution for getting all the `Issues` in the contract.
-
-**You also need to make the `getIssue` function `public`. The original spec called for it to be `external`.**
-
-
-```Solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
-
-contract FEWeightedVoting is ERC20 {
- using EnumerableSet for EnumerableSet.AddressSet;
-
- mapping(address => bool) claimed;
- uint public maxSupply = 1000000;
- uint totalClaimed;
-
- uint constant claimAmount = 100;
-
- error TokensClaimed();
- error AllTokensClaimed();
- error NoTokensHeld();
- error QuorumTooHigh(uint);
- error AlreadyVoted();
- error VotingClosed();
-
- enum Vote {
- AGAINST,
- FOR,
- ABSTAIN
- }
-
- struct Issue {
- EnumerableSet.AddressSet voters;
- string issueDesc;
- uint votesFor;
- uint votesAgainst;
- uint votesAbstain;
- uint totalVotes;
- uint quorum;
- bool passed;
- bool closed;
- }
-
- // EnumerableSets are mappings and cannot be returned outside a contract
- struct ReturnableIssue {
- address[] voters;
- string issueDesc;
- uint votesFor;
- uint votesAgainst;
- uint votesAbstain;
- uint totalVotes;
- uint quorum;
- bool passed;
- bool closed;
- }
-
- Issue[] issues;
-
- constructor(
- string memory _name,
- string memory _symbol
- ) ERC20(_name, _symbol) {
- // Burn Issue 0
- issues.push();
- }
-
- function claim() public {
- if (claimed[msg.sender] == true) {
- revert TokensClaimed();
- }
-
- if (totalSupply() >= maxSupply) {
- revert AllTokensClaimed();
- }
-
- _mint(msg.sender, claimAmount);
- claimed[msg.sender] = true;
- }
-
- function createIssue(
- string memory _issueDesc,
- uint _quorum
- ) public returns (uint) {
- if (balanceOf(msg.sender) == 0) {
- revert NoTokensHeld();
- }
-
- if (_quorum > totalSupply()) {
- revert QuorumTooHigh(_quorum);
- }
-
- Issue storage newIssue = issues.push();
- newIssue.issueDesc = _issueDesc;
- newIssue.quorum = _quorum;
- return issues.length - 1;
- }
-
- function getIssue(uint _id) public view returns (ReturnableIssue memory) {
- Issue storage issue = issues[_id];
- return
- ReturnableIssue(
- issue.voters.values(),
- issue.issueDesc,
- issue.votesFor,
- issue.votesAgainst,
- issue.votesAbstain,
- issue.totalVotes,
- issue.quorum,
- issue.closed,
- issue.passed
- );
- }
-
- function vote(uint _issueId, Vote _vote) public {
- Issue storage issue = issues[_issueId];
- if (issue.voters.contains(msg.sender)) {
- revert AlreadyVoted();
- }
- if (issue.closed) {
- revert VotingClosed();
- }
- issue.voters.add(msg.sender);
-
- if (_vote == Vote.FOR) {
- issue.votesFor += balanceOf(msg.sender);
- } else if (_vote == Vote.AGAINST) {
- issue.votesAgainst += balanceOf(msg.sender);
- } else if (_vote == Vote.ABSTAIN) {
- issue.votesAbstain += balanceOf(msg.sender);
- } else {
- revert("Error...");
- }
-
- issue.totalVotes += balanceOf(msg.sender);
-
- if (issue.totalVotes >= issue.quorum) {
- issue.closed = true;
- if (issue.votesFor > issue.votesAgainst) {
- issue.passed = true;
- }
- }
- }
-
- function numberOfIssues() public view returns(uint) {
- return issues.length;
- }
-
- function getAllIssues() public view returns(ReturnableIssue[] memory) {
- ReturnableIssue[] memory allIssues = new ReturnableIssue[](issues.length);
-
- for(uint i = 0; i < issues.length; i++) {
- allIssues[i] = getIssue(i);
- }
-
- return allIssues;
- }
-}
-```
-
----
-
-[RainbowKit]: https://www.rainbowkit.com/
-[wagmi]: https://wagmi.sh/
-[quick start]: https://www.rainbowkit.com/docs/installation/
-[Wallet Connectors]: ../frontend-setup/wallet-connectors/
-[`useAccount`]: https://wagmi.sh/react/hooks/useAccount
-[hydration error]: https://nextjs.org/docs/messages/react-hydration-error
-[ERC 20 Tokens Exercise]: https://docs.base.org/base-learn/docs/erc-20-token/erc-20-exercise
-[Sepolia BaseScan]: https://sepolia.basescan.org/
-[`useAccount` hook]: ./useAccount
-[Hardhat]: https://hardhat.org/
-[ABI]: https://docs.soliditylang.org/en/latest/abi-spec.html
-[`useReadContract`]: https://wagmi.sh/react/hooks/useReadContract
diff --git a/docs/learn/onchain-app-development/writing-to-contracts/useSimulateContract.mdx b/docs/learn/onchain-app-development/writing-to-contracts/useSimulateContract.mdx
deleted file mode 100644
index e16b217f8..000000000
--- a/docs/learn/onchain-app-development/writing-to-contracts/useSimulateContract.mdx
+++ /dev/null
@@ -1,110 +0,0 @@
----
-title: The `useSimulateContract` hook
-description: Improve your user experience with the `useSimulateContract` hook.
-hide_table_of_contents: false
----
-
-# The `useSimulateContract` hook
-The [`useSimulateContract`] hook simulates and validates a contract interaction without actually sending a transaction to the blockchain. Using it allows you to detect and respond to potential errors before the user tries to send a transaction.
-
----
-
-## Objectives
-
-By the end of this guide you should be able to:
-
-- Implement wagmi's `useSimulateContract` and `useWriteContract` to send transactions to a smart contract
-- Configure the options in `useSimulateContract` and `useWriteContract`
-- Call a smart contract function on-demand using the write function from `useWriteContract`, with arguments and a value
-
----
-
-## Refining the Claim Component
-
-In the previous step-by-step, you used [`useWriteContract`] to set up a hook you can use to call the `claim` function in your smart contract when the user clicks a button. The component works well enough, but it can take a long time for the wallet to pop up, particularly if there is network congestion. You also have no way of responding to a problem with the transaction inputs until after the user tries to initiate a transaction.
-
-### Using `useSimulateContract`
-
-The `useSimulateContract` can be used in partnership with `useWriteContract`. To do so, you set up the transaction parameters in `useSimulateContract`, then use the `data?.request` returned by it as an argument in the call to write to the contract. Modify your `TokenInfo` component to test it:
-
-```tsx
-const {
- data: claimData,
- isFetching: claimIsFetching,
- isError: claimIsError,
-} = useSimulateContract({
- address: contractData.address as `0x${string}`,
- abi: contractData.abi,
- functionName: 'claim',
-});
-
-useEffect(() => {
- if (claimIsError) {
- alert('Unable to claim'); // TODO: Better error handling
- }
-}, [claimIsError]);
-
-const { writeContract: claim, isPending: claimIsPending } = useWriteContract();
-
-const handleClaimClick = () => {
- claim(claimData?.request);
-};
-```
-
-You'll also need to update your handler to use the TypeScript pre-check feature, because the claim function will be briefly `undefined`.
-
-```tsx
-const handleClaimClick = () => {
- claim(claimData!.request);
-};
-```
-
-Reload the site and observe that the `alert` is triggered on load if you're signed in with an address that has already claimed tokens. You'll also see that the button is disabled, as though the user had clicked it and a transaction is loading in the wallet.
-
-### Making Adjustments
-
-The reason for this is a subtle difference in how `useWriteContract` and `useSimulateContract` work.
-
-In the last step-by-step, you saw how viem runs a simulation of the transaction when the `write` function is called. `useSimulateContract` eagerly runs this simulation and updates it's variables.
-
-You'll need to make some modifications for it to work. The `claimIsError` variable is being triggered when the data for the call is **simulated**, not when the call has settled. As a result, it immediately generates the error, and triggers the `alert` without requiring the user to click the button.
-
-You can solve this a number of ways, including simply not rendering the button if the user has already claimed. You could also modify the code, and combine it with `isError`, to share this information to the user.
-
-```tsx
-const {
- data: claimData,
- isFetching: claimIsFetching,
- isError: claimIsError,
-} = useSimulateContract({
- address: contractData.address as `0x${string}`,
- abi: contractData.abi,
- functionName: 'claim',
-});
-
-const { writeContract: claim, isPending: claimIsPending } = useWriteContract();
-
-return (
-
-
{claimIsFetching.toString()}
-
{'Token Balance: ' + tokenBalance}
-
-
{claimIsError ? 'Unable to claim tokens.' : 'Claim your tokens!'}
-
-);
-```
-
----
-
-## Conclusion
-
-In this step-by-step, you updated your app to use the `useSimulateContract` hook to provide a speedier wallet interaction for your users. You've also learned how you can predict and respond to potential errors without the user needing to attempt to send a transaction. You could use this functionality to let them know a username is already taken, a bid amount is not large enough, or an item is no longer available.
-
----
-
-[wagmi]: https://wagmi.sh/
-[`useWriteContract`]: https://wagmi.sh/react/hooks/useWriteContract
-[`useSimulateContract`]: https://wagmi.sh/react/hooks/useSimulateContract
-
diff --git a/docs/learn/onchain-app-development/writing-to-contracts/useWriteContract.mdx b/docs/learn/onchain-app-development/writing-to-contracts/useWriteContract.mdx
deleted file mode 100644
index da013c86a..000000000
--- a/docs/learn/onchain-app-development/writing-to-contracts/useWriteContract.mdx
+++ /dev/null
@@ -1,155 +0,0 @@
----
-title: The `useWriteContract` hook
-description: Write to your smart contracts with the `useWriteContract` hook.
-hide_table_of_contents: false
----
-
-# The `useWriteContract` hook
-
-The [`useWriteContract`] hook allows you to call your `public` and `external` smart contract functions that write to state and create a permanent modification to the data on chain.
-
----
-
-## Objectives
-
-By the end of this guide you should be able to:
-
-- Implement wagmi's `useWriteContract` hook to send transactions to a smart contract
-- Configure the options in `useWriteContract`
-- Display the execution, success, or failure of a function with button state changes, and data display
-
----
-
-## Sending a Transaction to the Blockchain
-
-
-In this step-by-step, you're going to start with the [`useWriteContract`] hook. You probably won't want to use this method in production. In the next step-by-step, we'll show you the [`useSimulateContract`] hook, how it works with `useWriteContract`, and how you can use it to create a better user experience.
-
-Exploring them separately will highlight the functionality provided by the prepare hook.
-
-
-
-In this module, you'll extend the onchain app you build in the previous module, [Reading and Displaying Data].
-
-
-You've built an app that can read from your Simple DAO smart contract, but so far, you've used BaseScan to send transactions that call your write functions. You can use the [`useWriteContract`] hook in a similar way to call those functions directly from your app.
-
-### Setting up the Component
-
-Add a new component called `TokenInfo` to the project, and a state variable for `tokenBalance`.
-
-```tsx
-import { useState } from 'react';
-
-export function TokenInfo() {
- const [tokenBalance, setTokenBalance] = useState(0);
-}
-```
-
-### Reading the Token Balance
-
-You'll need to know how many tokens the user has to be able to make decisions on what UI controls to display, so start by adding a `useReadContract`. You don't have a function for this directly in your contract, but your contract inherits from the [OpenZeppelin ERC20] contract, which has a function called `balanceOf` that takes an address and returns the balance for that address.
-
-You'll need the user's address to use in `args`, which you can conveniently get from the [`useAccount`] hook using the pattern below.
-
-```tsx
-const { data: balanceData, queryKey: balanceQueryKey } =
- useReadContract({
- address: contractData.address as `0x${string}`,
- abi: contractData.abi,
- functionName: "balanceOf",
- args: [useAccount().address],
- });
-
-useEffect(() => {
- if (balanceData) {
- setTokenBalance(balanceData as number);
- }
-}, [balanceData]);
-
-useEffect(() => {
- queryClient.invalidateQueries({ queryKey: balanceQueryKey });
-}, [blockNumber, queryClient]);
-```
-
-
-Remember, this is an expensive method to watch for data to change on the blockchain. In this case, a more production-suitable solution might be to call `balanceOf` after the user has done something that might change the balance.
-
-
-Set the `return` for your component to display this balance to the user:
-
-```tsx
-return (
-
-
{'Token Balance: ' + tokenBalance}
-
-);
-```
-
-Then, add the component to your app in `index.tsx`.
-
-```tsx
-return (
-
-
-
-
-
-
-
-);
-```
-
-Run the app and make sure you see the expected balance displayed on the page.
-
-### Setting up `useWriteContract`
-
-The [`useWriteContract`] hook is configured similarly to the [`useReadContract`] hook, with one important difference. You'll need to decompose the `write` property from the function call. This is a function that you can use to call your smart contract function whenever you'd like!
-
-```tsx
-const { writeContract: claim, isPending: claimIsPending } = useWriteContract();
-```
-
-Add an event handler function and a button. As with the `useReadContract` hook, you can use `isPending` and other state helpers to adjust your UI. The name of this one is a little misleading. `isPending` will be `true` starting from the moment the transaction gets sent to the user's wallet.
-
-You can use this to nudge them to look to their wallet to complete the transaction. Additionally, add a `useEffect` to watch for an error state.
-
-```tsx
-const handleClaimClick = () => {
- claim({
- abi: contractData.abi,
- address: contractData.address as `0x${string}`,
- functionName: 'claim',
- });
-};
-
-return (
-
-
{'Token Balance: ' + tokenBalance}
-
-
-);
-```
-
-Try it out. Notice that the button text briefly changes without the wallet window popping up if you click the `Claim Tokens` button while connected with a wallet that already owns the tokens. The reason this happens is that viem, which underlies wagmi, runs a simulation of the transaction to estimate gas costs. If that simulation fails, it triggers the fail mechanism immediately, rather than allowing the app to send a bad transaction to the blockchain and cost the user gas for a call doomed to fail. You will fix this in the next tutorial.
-
-In the meantime, you'll need to change to a new wallet or redeploy your contract a couple of times to complete your testing. Do that, and try out the call on a wallet that hasn't claimed any tokens. Notice that the button is disabled and the text now prompts the user to look to their wallet to approve the transaction.
-
----
-
-## Conclusion
-
-In this step-by-step, you've learned how to use the [`useWriteContract`] hook to call your smart contract functions on demand. You've also tested methods to manage the UI/UX experience for your users, based on the state of the transaction, as well as its success or failure.
-
----
-
-[wagmi]: https://wagmi.sh/
-[`useWriteContract`]: https://wagmi.sh/react/hooks/useWriteContract
-[`usePrepareContractWrite`]: https://wagmi.sh/react/prepare-hooks/usePrepareContractWrite
-[Reading and Displaying Data]: ../reading-and-displaying-data/useAccount
-[`useReadContract`]: https://wagmi.sh/react/hooks/useReadContract
-[OpenZeppelin ERC20]: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
-[`useAccount`]: https://wagmi.sh/react/hooks/useAccount
-
diff --git a/docs/learn/onchain-concepts/building-onchain-ai.mdx b/docs/learn/onchain-concepts/building-onchain-ai.mdx
deleted file mode 100644
index 2584493f0..000000000
--- a/docs/learn/onchain-concepts/building-onchain-ai.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
----
-title: "AI Onchain"
-description: "Learn how AI agents can manage economic value onchain using AgentKit to deploy contracts, create tokens, and manage funds autonomously on Base."
----
-
-AI agents are quickly evolving, but most can't hold or manage economic value on their own. AgentKit bridges that gap by giving AI agents a wallet and "economic agency," letting them deploy contracts, create tokens, or manage funds autonomously on Base. In this final sub-section, you'll learn about practical use cases for combining AI with onchain functionality—and how AgentKit simplifies the process.
-
-
-**AI + Blockchain Challenge**: AI agents can read the web, but they typically can't manage crypto or execute onchain tasks.
-
-
-
-**AgentKit Solution**: Gives AI agents "economic agency" by integrating them with onchain wallets, letting them deploy contracts, transfer tokens, and carry out tasks on Base autonomously. Bridge AI (LLMs) with crypto in a secure, straightforward way.
-
diff --git a/docs/learn/onchain-concepts/building-onchain-frontend-development.mdx b/docs/learn/onchain-concepts/building-onchain-frontend-development.mdx
deleted file mode 100644
index 2f4b780c9..000000000
--- a/docs/learn/onchain-concepts/building-onchain-frontend-development.mdx
+++ /dev/null
@@ -1,34 +0,0 @@
----
-title: "Connecting Your Frontend"
-description: "Learn how onchain frontends differ from Web2 by managing wallet connections, displaying real-time onchain data, and handling transaction flows using OnchainKit, wagmi, and Viem."
----
-
-Unlike Web2 frontends that primarily handle user input and REST API calls, onchain frontends must manage wallet connections, display onchain data in real time, and handle transaction flows. Fortunately, libraries like OnchainKit, wagmi, and Viem help you abstract away much of the complexity. Here, you'll learn how to easily support multiple wallet types, show transaction status, and seamlessly integrate onchain actions into a modern UI.
-
-## What's Unique for Onchain Frontends?
-
-
-Onchain frontends must handle unique requirements that don't exist in traditional Web2 applications:
-- Wallet connections (Coinbase Wallet,MetaMask, Smart Wallet, etc.)
-- Contract calls and transaction state management
-- Token and NFT interactions
-
-
-## OnchainKit
-
-OnchainKit provides a comprehensive set of React components and TypeScript utilities for building onchain applications:
-
-
-**Wallet Connection Flows**: Supporting multiple wallet types seamlessly
-
-
-
-**Common Onchain Actions**: Swapping tokens, minting NFTs, sending transactions
-
-
-
-**Integration Ready**: Works with popular client libraries like [wagmi](https://wagmi.sh/react/getting-started) and [Viem](https://viem.sh/docs/getting-started), connecting to the Base Node under the hood so you don't worry about low-level RPC details
-
-
-[wagmi]: https://wagmi.sh/react/getting-started
-[Viem]: https://viem.sh/docs/getting-started
diff --git a/docs/learn/onchain-concepts/building-onchain-gas.mdx b/docs/learn/onchain-concepts/building-onchain-gas.mdx
deleted file mode 100644
index d5e50468b..000000000
--- a/docs/learn/onchain-concepts/building-onchain-gas.mdx
+++ /dev/null
@@ -1,22 +0,0 @@
----
-title: "Paying for Onchain Transactions (Gas)"
-description: "Learn how Paymaster services address gas fee barriers by sponsoring transactions or allowing users to pay fees in alternative tokens, improving user onboarding on Base."
----
-
-Transactions on blockchain networks typically require "gas" fees, which can be a major hurdle for new users without pre-funded wallets. Paymaster services address this by letting developers sponsor gas or allowing users to pay fees in tokens other than ETH. In this sub-section, we explore how Paymaster works on Base, how it improves user onboarding, and how you can implement it in your own application.
-
-## The Challenge
-
-
-**Gas Fee Barriers**: Users must have gas tokens (ETH on Base) to pay fees. This can be a significant hurdle for newcomers who have zero crypto.
-
-
-## Paymaster Solution
-
-
-Paymaster is a service that sponsors gas fees on behalf of users, enabling gasless transactions. This allows your users to transact without needing ETH in their wallet, or to pay gas in alternative tokens like USDC.
-
-
-
-Base's [Paymaster solution](https://www.coinbase.com/developer-platform/products/paymaster) (via CDP) streamlines onboarding and can significantly improve user experience.
-
diff --git a/docs/learn/onchain-concepts/building-onchain-identity.mdx b/docs/learn/onchain-concepts/building-onchain-identity.mdx
deleted file mode 100644
index 2482ea9eb..000000000
--- a/docs/learn/onchain-concepts/building-onchain-identity.mdx
+++ /dev/null
@@ -1,41 +0,0 @@
----
-title: "Giving Onchain Accounts an Identity"
-description: "Learn how Basenames and onchain verifications make blockchain addresses human-readable and trustworthy, improving user experience and app functionality."
----
-
-While blockchains publicly display wallet addresses by default, these long strings are not friendly for everyday use. Onchain naming systems like Basenames solve this by letting users register human-readable names (for example, "alice.base"). Beyond simple naming, onchain identity can be extended through verifications and attestations that prove additional facts (e.g., that a user is a verified account holder or resides in a specific country). Here, you'll learn how Basenames and onchain verifications work together to make your app more user-friendly and trustworthy.
-
-## Why Identity Matters
-
-
-**Address Readability**: By default, wallet addresses are long, unreadable hex strings. Onchain names (like "alice.base") make it easier to identify recipients or users.
-
-
-
-**Trust Without Privacy Risk**: Attestations/Verifications allow your app to trust certain user attributes—without exposing personal data offchain.
-
-
-## Basenames
-
-
-**Human-Readable Names** for addresses on Base (akin to DNS for IPs)
-
-
-
-**Unified Identity**: Users can register names, add a profile picture, and display them across all apps they connect to
-
-
-
-**Reduced Friction**: Helps unify an onchain identity and reduce user friction
-
-
-## Onchain Verifications
-
-
-**The Challenge**: The chain can't natively see offchain data (e.g., your nationality or membership status).
-
-
-
-**The Solution**: Verifications let you post attestations **onchain** so other contracts can read them without privacy risks. For example, prove you're a Coinbase user, or a resident of a certain country, and automatically unlock special privileges in dapps.
-
-
diff --git a/docs/learn/onchain-concepts/building-onchain-nodes.mdx b/docs/learn/onchain-concepts/building-onchain-nodes.mdx
deleted file mode 100644
index fca22af15..000000000
--- a/docs/learn/onchain-concepts/building-onchain-nodes.mdx
+++ /dev/null
@@ -1,75 +0,0 @@
----
-title: "Interacting with the Chain (Node Access)"
-description: "Learn how blockchain node access works via RPC endpoints, comparing self-hosted vs hosted options, and how to integrate Base Node for stable onchain interactions."
----
-
-Every time you read or write data on Base, you're making calls to a blockchain node via RPC (Remote Procedure Call) endpoints. You can host your own node or rely on a service like Base Node (via CDP). This sub-section explains why node access is crucial, compares self-hosted vs. hosted node options, and shows you how to integrate an RPC endpoint in your application for stable, scalable onchain interactions.
-
-## Why You Need a Node
-
-
-Under the hood, apps read/write onchain data by making RPC calls to a blockchain node. You can self-host or use a managed service.
-
-
-Every blockchain interaction requires communication with a node that maintains the complete blockchain ledger. Whether you're checking wallet balances, sending transactions, or reading smart contract state, your application communicates through standardized JSON-RPC methods like `eth_getBalance`, `eth_sendTransaction`, and `eth_call`.
-
-## RPC Endpoints Overview
-
-Base provides public RPC endpoints for development and testing:
-
-| Network | RPC Endpoint | Chain ID | Use Case |
-| :------ | :----------- | :------- | :------- |
-| Base Mainnet | `https://mainnet.base.org` | 8453 | Development only |
-| Base Sepolia | `https://sepolia.base.org` | 84532 | Testing only |
-
-
-These public endpoints are **rate-limited** and **not suitable for production systems**. For production applications, use a dedicated node provider or run your own node.
-
-
-## Hosted Node Providers
-
-For production applications, choose from Base's ecosystem of trusted node providers:
-
-### Coinbase Developer Platform (CDP)
-**Base Node** (via CDP) provides high-throughput access with a free tier available, plus enterprise-grade options. Built on the same infrastructure powering Coinbase's retail exchange for maximum reliability.
-
-### Popular Provider Options
-- **Alchemy**: Enhanced features, SDKs, free tier with robust JSON-RPC APIs
-- **QuickNode**: Discover Plan with optional "Trace Mode" and "Archive Mode" add-ons
-- **Chainstack**: Elastic RPC nodes with geographically diverse, protected API endpoints
-- **Ankr**: Globally distributed decentralized network with free and paid tiers
-- **OnFinality**: High-performance archive access with generous free tier and high rate limits
-- **Dwellir**: Archive RPC access with a 25 million response free tier
-
-
-Most providers offer both mainnet and testnet (Sepolia) access. Compare pricing, rate limits, and features like archive data access when selecting a provider.
-
-
-## Self-Hosted Base Node
-
-### When to Run Your Own Node
-Consider self-hosting when you need:
-- Complete control over node configuration
-- No external dependencies or rate limits
-- Archive data access for historical queries
-- Custom monitoring and analytics
-
-### Hardware Requirements
-Running a Base node requires significant resources:
-
-- **CPU**: 8-Core processor with good single-core performance
-- **RAM**: Minimum 16 GB (32 GB recommended)
-- **Storage**: NVMe SSD with adequate capacity for chain data plus snapshots
- - Calculate: `(2 × current_chain_size) + snapshot_size + 20% buffer`
-- **Network**: Stable internet connection with good bandwidth
-
-
-Running a node is **time-consuming**, **resource-expensive**, and **potentially costly**. Syncing can take days and consume significant bandwidth.
-
-
-### Performance Considerations
-- **Reth vs Geth**: Reth provides significantly better performance in Base's high-throughput environment
-- **Archive Nodes**: Geth archive nodes are no longer supported; use Reth for archive functionality
-- **Snapshots**: Weekly snapshots available to accelerate initial sync process
-- **Storage**: Local NVMe SSDs strongly recommended over networked storage
-
diff --git a/docs/learn/onchain-concepts/building-onchain-onramps.mdx b/docs/learn/onchain-concepts/building-onchain-onramps.mdx
deleted file mode 100644
index 6eb488154..000000000
--- a/docs/learn/onchain-concepts/building-onchain-onramps.mdx
+++ /dev/null
@@ -1,22 +0,0 @@
----
-title: "Funding Wallets (Onramps)"
-description: "Learn how onramps like MagicSpend and CDP Onramp solve the fiat-to-crypto conversion challenge, enabling smooth user onboarding with FundButton components."
----
-
-Before users can transact on Base, they need crypto in their wallets. Historically, this required lengthy steps via exchanges and bank transfers. Now, onramps like MagicSpend and CDP Onramp (and the drop-in FundButton components from OnchainKit) make this process faster and smoother. In this sub-section, discover how to integrate user-friendly fiat-to-crypto solutions directly into your app, reducing churn and lowering barriers to entry.
-
-## The Funding Challenge
-
-
-**User Barrier**: A user can't easily transact unless they have some crypto in their wallet. Traditional workflows are clunky (bank → CEX → withdraw to wallet).
-
-
-## Base Onramp Solutions
-
-
-**MagicSpend** and **CDP Onramp** enable quick conversions from fiat to onchain funds without forcing users to leave your app.
-
-
-
-**Easy Integration**: Components like **FundButton** or **FundCard** in OnchainKit let you drop in funding flows directly into your user interface.
-
diff --git a/docs/learn/onchain-concepts/building-onchain-social-networks.mdx b/docs/learn/onchain-concepts/building-onchain-social-networks.mdx
deleted file mode 100644
index c31dfcf40..000000000
--- a/docs/learn/onchain-concepts/building-onchain-social-networks.mdx
+++ /dev/null
@@ -1,52 +0,0 @@
----
-title: "Onchain Social Networks"
-description: "Discover how onchain social networks like Farcaster enable apps to tap into existing social graphs, solving the 'empty network' problem for new social applications."
----
-
-Traditional social media apps must build their user bases from scratch and can risk losing access to platform APIs. In contrast, onchain social networks (e.g., Farcaster) allow your app to tap into existing social graphs and user relationships immediately. This part covers how open social protocols foster collaboration, prevent centralized gatekeeping, and eliminate the "empty network" problem for new social apps.
-
-## Open Social Layers
-
-
-**Instant Network Effect**: Rather than building siloed social graphs from scratch, you can tap into existing onchain social networks like Farcaster.
-
-
-
-**Immediate Access**: If your user is on Farcaster, your app automatically gains access to their followers, social feed, and social connections.
-
-
-
-**Solves Empty Network Problem**: This provides content and user relationships from day one, eliminating the classic "empty network" challenge.
-
-
-## Mini Apps
-
-Mini Apps are lightweight web applications that run natively within Farcaster clients, providing rich interactive experiences without requiring users to leave their social feeds.
-
-### What Makes Mini Apps Powerful
-- **Native Integration**: Launch instantly from social posts without downloads or redirects
-- **Social Context**: Automatic access to user identity, connections, and social graph
-- **Onchain Capabilities**: Direct wallet connections and blockchain interactions
-- **Cross-Platform**: Works in Farcaster clients like Farcaster and Coinbase Wallet
-
-
-### Mini App Capabilities
-**User Interactions**
-- Send notifications to users who have added your app
-- Access user's Farcaster identity and social connections
-- Enable users to share content back to their feeds
-
-**Onchain Integration**
-- Direct wallet connections within the social context
-- Execute transactions without leaving the app
-- Integration with Base blockchain and DeFi protocols
-
-**Social Features**
-- Compose and share casts from within your app
-- View and interact with user profiles
-- Access social graph data for personalized experiences
-
-
-**Viral Distribution**: Mini Apps spread organically through social sharing, providing built-in user acquisition as users share their experiences directly in their social feeds.
-
-
diff --git a/docs/learn/onchain-concepts/building-onchain-wallets.mdx b/docs/learn/onchain-concepts/building-onchain-wallets.mdx
deleted file mode 100644
index 1f84b0f76..000000000
--- a/docs/learn/onchain-concepts/building-onchain-wallets.mdx
+++ /dev/null
@@ -1,46 +0,0 @@
----
-title: "Onchain Accounts (Wallets)"
-description: "Learn about onchain accounts, from traditional EOAs using private keys to modern Smart Wallets with passkeys, and how Base's Smart Wallet SDK simplifies integration."
----
-
-Onchain accounts, often called "wallets", are the starting point for any onchain interaction. Unlike traditional accounts that store credentials in a database, onchain accounts authenticate and authorize transactions on the public network with credentials held by the user. The original onchain accounts (EOA's) use private keys backed up by a sequence of 24 random words. Recently, with the introduction of modern accounts like Smart Wallet, the credentials are Passkeys which provide improved security and recoverability. This sub-section covers these two main wallet types, the differences in user experience and security, and how Base's Smart Wallet SDK makes them easier to integrate.
-
-## What's Different?
-
-
-Every onchain action requires an account (a wallet) to sign transactions. Onchain accounts can **hold funds** and also **authorize** transactions on a blockchain.
-
-
-## Types of Onchain Accounts
-
-### Externally Owned Accounts (EOAs)
-
-
-**Traditional Model**: Private key + seed phrase model (e.g., MetaMask, Rabby, Ledger)
-
-
-
-**User Experience**: Commonly used today but can be cumbersome or risky for new users
-
-
-
-**Capabilities**: Holds digital assets but has no account level logic. Required for deploying smart contracts.
-
-
-### Smart Wallets
-
-
-**Modern Approach**: Modern accounts with their own logic enabling improved security, recoverability, and functionality.
-
-
-
-**Enhanced Features**: Built-in recovery features, passkey authentication, and improved onboarding experience for users
-
-
-
-**Advanced Permissions**: Can include features like "SpendPermissions" for fine-grained security and improved user experience
-
-
-## Learn More
-
-If you'd like a deeper dive on EOAs vs. smart wallets, check out our [Smart Wallet docs](/smart-wallet/concepts/what-is-smart-wallet) and recommended reading on [Account Abstraction](https://www.alchemy.com/learn/account-abstraction). Base provides an [SDK](/smart-wallet/technical-reference/sdk) to help you integrate Smart Wallets easily.
diff --git a/docs/learn/onchain-concepts/continue-building-onchain.mdx b/docs/learn/onchain-concepts/continue-building-onchain.mdx
deleted file mode 100644
index 18fc36d7c..000000000
--- a/docs/learn/onchain-concepts/continue-building-onchain.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
----
-title: "Final Thoughts & Next Steps"
-description: "Take your next steps in building onchain with Base's tools including Base Node, OnchainKit, Paymaster, AgentKit, and Basenames for complete onchain development."
----
-
-Building onchain with Base brings a new dimension to your apps—trust-minimized logic, global accessibility, and open integrations with existing ecosystems (DeFi, social, AI, etc.). Now that you have the big picture, here are some suggested next steps:
-
-* [Spin up your first Base Node endpoint](https://portal.cdp.coinbase.com/products/node)
-* [Use OnchainKit to build a basic wallet connection flow](https://onchainkit.xyz/)
-* [Integrate Paymaster for gasless transactions](https://docs.cdp.coinbase.com/paymaster/docs/welcome)
-* [Explore AI Onchain with AgentKit](https://docs.cdp.coinbase.com/agentkit/docs/welcome)
-* [Set up your Basename for a friendlier identity](https://www.base.org/names)
-
-Happy Building!
diff --git a/docs/learn/onchain-concepts/core-concepts.mdx b/docs/learn/onchain-concepts/core-concepts.mdx
deleted file mode 100644
index 4f4c6bec0..000000000
--- a/docs/learn/onchain-concepts/core-concepts.mdx
+++ /dev/null
@@ -1,21 +0,0 @@
----
-title: "Core Concepts"
-description: "Learn the unique aspects of building onchain applications with Base's tools like OnchainKit, Paymaster, and Smart Wallet to tap into global payment networks and blockchain ecosystems."
----
-
-Building onchain means tapping into a global, fast, and cheap payment network, plus a thriving ecosystem of NFTs, games, social networks, and more. This section will introduce the **unique aspects** of building onchain and show you **how Base's tools** (e.g., OnchainKit, Paymaster, Smart Wallet, etc.) simplify that process.
-
-If you'd like to learn more about the broader vision and benefits, check out Why Base. If you want to **start building right away**, visit our Quickstart.
-
-We will cover the following:
-
-- [The Onchain Tech Stack](understanding-the-onchain-tech-stack)
-- [Unique aspects of building onchain (and the tools that help)](onchain-aspects)
- - [Onchain Accounts (Wallets)](building-onchain-wallets)
- - [Onchain Identity](building-onchain-identity)
- - [Paying for transactions](building-onchain-gas)
- - [Connecting your Frontend](building-onchain-frontend-development)
- - [Funding Wallets (Onramps)](building-onchain-onramps)
- - [Onchain Social Networks](building-onchain-social-networks)
- - [AI Onchain](building-onchain-ai)
-- [The Onchain Development Flow](development-flow)
diff --git a/docs/learn/onchain-concepts/development-flow.mdx b/docs/learn/onchain-concepts/development-flow.mdx
deleted file mode 100644
index 5d7404633..000000000
--- a/docs/learn/onchain-concepts/development-flow.mdx
+++ /dev/null
@@ -1,90 +0,0 @@
----
-title: "The Development Flow"
-description: "Learn the different approaches to onchain development, from client-side apps using OnchainKit to smart contract development with Hardhat and Foundry."
----
-
-## Client-side Development
-
-If you're focusing on the front end or building a user interface, Kits like OnchainKit provide an opinionated, model-agnostic setup for handling user authentication, contract interactions, and transaction status updates. With a client-side approach:
-
-
-
- Configure an RPC endpoint to read chain data and interact with the blockchain.
-
-
-
- Use OnchainKit (or an equivalent framework) to handle function calls, sign messages, or sponsor gas if you've integrated Paymaster solutions.
-
-
-
- Add Identity Solutions like Basenames or Onchain Profiles for more user-friendly experiences.
-
-
-
-
-## Smart Contract Development
-
-Base is an EVM-equivalent chain, so you can build or reuse your smart contracts written in Solidity. Smart wallets, Paymaster services, and other infrastructure components on Base can significantly streamline the user experience around your onchain programs.
-
-
-
- Select a development environment such as Hardhat or Foundry.
-
-
-
- Write and compile your contracts in Solidity.
-
-
-
- Test locally or on a testnet before pushing to mainnet.
-
-
-
- Deploy to Base using your EOA wallet (or specialized deployment tooling).
-
-
-
-### Smart Contract Guides
-
-
-
- Quickstart: Deploy on Base
-
-
-
- Foundry Tutorial
-
-
-
- Base Learn: Development with HardHat
-
-
-
- Base Learn: Development with Foundry
-
-
-
- Verify a Smart Contract using Basescan API
-
-
-
-## Developer Environments
-
-Base offers multiple environments to help you iterate quickly:
-
-
-
-**Production Environment**
-Where real-value transactions occur. Use for live applications.
-
-
-
-**Base testnet on Sepolia**
-Great for QA or staging. Test your applications without real value.
-
-
-
-**Local EVM Environment**
-You can run a local EVM environment with Hardhat or similar tools to test your contracts before deploying.
-
-
diff --git a/docs/learn/onchain-concepts/understanding-the-onchain-tech-stack.mdx b/docs/learn/onchain-concepts/understanding-the-onchain-tech-stack.mdx
deleted file mode 100644
index d4155fa96..000000000
--- a/docs/learn/onchain-concepts/understanding-the-onchain-tech-stack.mdx
+++ /dev/null
@@ -1,73 +0,0 @@
----
-title: "Understanding Onchain in Your Tech Stack"
-description: "Learn how blockchain technology augments traditional web stacks by adding globally accessible logic and data through smart contracts and onchain state."
----
-
-In broad strokes, a traditional web application has a frontend, a backend, and a database. When we talk about "building onchain," we're adding a **blockchain** to that tech stack as globally accessible infrastructure that can provide both **logic** (smart contracts) and **data** (onchain state).
-
-## Traditional vs Onchain Architecture
-
-
-
-- **Frontend**: Usually React, Vue, or another framework
-- **Backend**: Node.js, Python, or similar, handling business logic
-- **Database**: A solution like Postgres or MongoDB for persistent storage
-
-
-
-- **Smart Contracts**: Globally accessible logic
-- **Onchain State**: Data that lives on Base (balances, user profiles, etc.) and is accessible to any onchain or offchain application
-
-
-
-
-Integrating a blockchain doesn't necessarily **replace** your database or your entire backend. Instead, it augments your stack. For instance, some business logic might remain in your backend, and sensitive data may remain in a traditional database.
-
-
-## What belongs onchain?
-
-## Onchain State
-
-Onchain state has several notable properties:
-
-
-**Immutable**: Once recorded, data can't be deleted ensuring a permanent historical record
-
-
-
-**Transparent**: All data is publicly viewable and verifiable
-
-
-
-**Global**: Data is stored across multiple nodes, eliminating single points of failure
-
-
-
-**Expensive**: Storing data onchain is expensive because it must be stored by every node globally
-
-
-Because of these properties, carefully consider what data is stored onchain. Ask yourself if the data is necessary for onchain logic or if the data should be immutable. Alternatively, you may be able to store your data offchain in a traditional database or a decentralized file storage network such as [IPFS](https://ipfs.tech/). If the state change is more informational, it may be best to emit an event, providing a more cost-effective alternative to direct onchain storage.
-
-## Onchain Events
-
-
-Since storing data onchain is expensive, we oftentimes use a cheaper alternative: events. Events are not part of the state, which makes them optional to store when running a node. They are a lot cheaper to use than state storage.
-
-
-Events are triggered when an onchain method is called. Events can have different data formats. In order to make sure events follow the format we are looking to receive on the frontend, we often use data indexers. For that, you can build your own indexer or use one of the [existing tools](/base-chain/tools/data-indexers).
-
-## Onchain Logic
-
-Onchain logic is stored in smart contracts. Similar to onchain data, it is immutable, transparent, and global. By default, onchain logic is accessible to every network participant, but can be permissioned so that only certain participants can interact. This makes onchain logic highly composable where builders can leverage existing onchain logic and extend that logic in novel ways instead of building from scratch.
-
-
-**Capabilities**: Transfer funds and digital assets, create onchain games, create content and social posts onchain, and more. Pretty much anything you can do offchain can be done onchain.
-
-
-
-**Cost Consideration**: Because every node in the network needs to execute every transaction, each transaction has a cost dependent on the computational expense of the onchain logic being run in that transaction.
-
-
-
-If you have proprietary logic which you don't want public, this shouldn't be onchain. Or if the logic doesn't need to be decentralized and is computationally expensive, it may be better kept in a traditional backend opposed to onchain.
-
diff --git a/docs/learn/solidity/anatomy.mdx b/docs/learn/solidity/anatomy.mdx
deleted file mode 100644
index 3f4da9254..000000000
--- a/docs/learn/solidity/anatomy.mdx
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Anatomy of a Smart Contract'
----
-
-[Content explaining the structure and components of a smart contract]
-k
\ No newline at end of file
diff --git a/docs/learn/solidity/basic-types.mdx b/docs/learn/solidity/basic-types.mdx
deleted file mode 100644
index 5284eb2f4..000000000
--- a/docs/learn/solidity/basic-types.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Basic Types'
----
-
-[Overview of basic data types in Solidity]
diff --git a/docs/learn/solidity/deployment-in-remix.mdx b/docs/learn/solidity/deployment-in-remix.mdx
deleted file mode 100644
index 4334aa730..000000000
--- a/docs/learn/solidity/deployment-in-remix.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Deployment in Remix'
----
-
-[Guide for deploying contracts using Remix]
diff --git a/docs/learn/solidity/exercise-basics.mdx b/docs/learn/solidity/exercise-basics.mdx
deleted file mode 100644
index 94b14fa7c..000000000
--- a/docs/learn/solidity/exercise-basics.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Exercise'
----
-
-[Practice exercises for basic Solidity concepts]
diff --git a/docs/learn/solidity/hello-world.mdx b/docs/learn/solidity/hello-world.mdx
deleted file mode 100644
index cd40a4f2c..000000000
--- a/docs/learn/solidity/hello-world.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Hello World Guide'
----
-
-[Guide for creating your first smart contract]
diff --git a/docs/learn/solidity/introduction-to-contracts.mdx b/docs/learn/solidity/introduction-to-contracts.mdx
deleted file mode 100644
index 1e941623e..000000000
--- a/docs/learn/solidity/introduction-to-contracts.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Introduction to Contracts'
----
-
-[Introduction to smart contract development]
diff --git a/docs/learn/solidity/introduction-to-remix.mdx b/docs/learn/solidity/introduction-to-remix.mdx
deleted file mode 100644
index 12e76a947..000000000
--- a/docs/learn/solidity/introduction-to-remix.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Introduction to Remix'
----
-
-[Content introducing the Remix IDE]
diff --git a/docs/learn/solidity/introduction.mdx b/docs/learn/solidity/introduction.mdx
deleted file mode 100644
index c73e7fb4b..000000000
--- a/docs/learn/solidity/introduction.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Introduction to Solidity'
----
-
-[Content introducing Solidity programming language]
diff --git a/docs/learn/solidity/overview.mdx b/docs/learn/solidity/overview.mdx
deleted file mode 100644
index 40edc5a9a..000000000
--- a/docs/learn/solidity/overview.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Overview'
----
-
-[Overview of Solidity programming concepts]
diff --git a/docs/learn/solidity/remix-guide.mdx b/docs/learn/solidity/remix-guide.mdx
deleted file mode 100644
index 036b26802..000000000
--- a/docs/learn/solidity/remix-guide.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Remix Guide'
----
-
-[Detailed guide for using Remix IDE]
diff --git a/docs/learn/solidity/step-by-step.mdx b/docs/learn/solidity/step-by-step.mdx
deleted file mode 100644
index 343fcea06..000000000
--- a/docs/learn/solidity/step-by-step.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Step by Step Guide'
----
-
-[Step-by-step guide for Solidity development]
diff --git a/docs/learn/solidity/video-tutorial.mdx b/docs/learn/solidity/video-tutorial.mdx
deleted file mode 100644
index c219cc257..000000000
--- a/docs/learn/solidity/video-tutorial.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Video Tutorial'
----
-
-[Video tutorial content for Solidity introduction]
diff --git a/docs/learn/storage/how-storage-works-video.mdx b/docs/learn/storage/how-storage-works-video.mdx
deleted file mode 100644
index 6e456fd27..000000000
--- a/docs/learn/storage/how-storage-works-video.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: How Storage Works
-description: Learn how storage works in the EVM.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
-
diff --git a/docs/learn/storage/how-storage-works.mdx b/docs/learn/storage/how-storage-works.mdx
deleted file mode 100644
index 133552151..000000000
--- a/docs/learn/storage/how-storage-works.mdx
+++ /dev/null
@@ -1,237 +0,0 @@
----
-title: How Storage Works
-sidebarTitle: Storage Overview
-description: An introduction to how storage works in Ethereum
-hide_table_of_contents: false
----
-
-In this article, we will delve into the workings of Ethereum storage, explore the nuances of variable declaration ordering, and provide examples of efficient and inefficient storage practices to create optimized smart contracts.
-
----
-
-## Objectives:
-
-By the end of this lesson you should be able to:
-
-- Diagram how a contract's data is stored on the blockchain (Contract -> Blockchain)
-- Order variable declarations to use storage efficiently
-- Diagram how variables in a contract are stored (Variable -> Contract)
-
----
-
-## Introduction
-
-Creating smart contracts that can operate efficiently requires a thorough understanding of how storage works in Ethereum. When designing a contract, you need to consider the storage requirements of the contract, including the types of storage needed, the gas costs associated with storage operations, and how to manage storage effectively. Poor storage management practices can lead to bloated contracts that consume excessive gas, making them more expensive to execute. By following best practices for storage management, you'll be equipped to create contracts that are lean, efficient, and cost-effective.
-
----
-
-## Smart Contract Data Storage
-
-### Key-Value Store
-
-Smart contracts on Ethereum store and manage data utilizing a key-value store model, where each piece of data is identified by a unique key and accompanied by its corresponding value.
-
-In this diagram, the keys (user addresses) are unique identifiers used to index the corresponding values (balances):
-
-
-
-
-
-This model can be compared to a dictionary or a map where the key serves as the index and the value represents the data associated with that index. However, the key-value store has distinct characteristics that set it apart from these traditional data structures, which make it a more optimal choice for smart contracts on Ethereum.
-
-- **Simplicity:** It is simple and straightforward, which allows for easier implementation and maintenance within a contract.
-
-- **Scalability:** It is highly scalable, making it well-suited for managing vast amounts of data typically associated with apps and smart contracts. This scalability helps maintain performance levels even as data storage requirements grow.
-
-- **Fixed-size chunks:** Storing data in fixed-size 32-byte chunks optimizes storage space and ensures that data location calculations are more efficient. This feature is particularly beneficial in the context of Ethereum, where storage costs are a significant concern.
-
-- **Efficient storage and retrieval:** It is optimized for storing and retrieving large volumes of data efficiently, which is essential for quick access to stored information.
-
-- **Security and immutability:** Unlike other storage models that may allow direct data manipulation, key-value stores within Ethereum's environment ensure data integrity and security through transaction-based modifications. This feature aligns with the decentralized and trustless nature of blockchain technology.
-
-- **Gas-efficiency:** In Ethereum, every operation within a smart contract execution consumes gas. The key-value store model is designed to be gas-efficient, minimizing the gas consumption for storage and retrieval operations, thus reducing the overall cost of contract execution.
-
-- **Compatibility with decentralized environments:** It is particularly suitable for decentralized environments, where data consistency, integrity, and security are crucial. The model's design inherently addresses the challenges posed by multi-threaded or concurrent environments where multiple processes or functions may attempt to access or modify the same data simultaneously.
-
-### Types of Storage
-
-There are three primary types of storage in Ethereum smart contracts: storage, memory, and stack. Each type has its specific use case and characteristics, which make them suitable for different aspects of smart contract execution.
-
-#### Storage
-
-Storage is the most persistent and expensive form of data storage. Data stored in the contract's storage persists across transaction executions and is accessible to any function within the smart contract. This storage is also visible on the blockchain and can be read by external sources, making it suitable for storing important and long-lasting information related to the contract's state.
-
-Key attributes of storage:
-
-- **Persistent:** Data remains in storage even after the contract execution finishes, allowing for state continuity across multiple transactions.
-
-- **Expensive:** Storing and modifying data in storage consumes more gas compared to other data locations, making it costly in terms of transaction fees.
-
-- **Visible on the blockchain:** Storage data is publicly available and can be read by external parties.
-
-Consider the following contract:
-
-```solidity
-contract StorageDemo {
- // Declare a state variable to store data in storage
- uint256 public storedData;
-
- // Function to update the storedData variable in storage
- function updateData(uint256 newData) public {
- storedData = newData;
- }
-}
-```
-
-The contract includes a state variable called `storedData`, which is stored in the contract's storage. The `public` visibility modifier allows anyone to access this variable. The contract also includes a public function called `updateData`, which can be called by anyone to modify the value of `storedData` in storage.
-
-Any changes made to `storedData` in storage will persist across multiple transactions and will be visible to anyone who reads the blockchain. Please note that storage is more expensive than other data locations, so it is important to use it judiciously to minimize gas costs.
-
-#### Memory
-
-Memory is a temporary and more affordable data location. It's used to save data during the execution of a single transaction. Once the transaction is complete, the memory is wiped clean and any data within it is lost. Memory is suitable for storing intermediate variables and temporary data that does not need to persist across multiple transactions.
-
-Key attributes of memory:
-
-- **Temporary:** Data in memory is only available during a single transaction execution and is lost afterward.
-
-- **Less expensive:** Saving and modifying data in memory consumes less gas compared to storage, making it more cost-effective for temporary data.
-
-- **Not visible on the blockchain:** Memory data is not accessible to external parties and remains confined to the transaction execution.
-
-Consider the following contract:
-
-```solidity
-contract MemoryDemo {
- // Declare a state variable to store data in storage
- uint256 public storedData;
-
- // Function to update the storedData variable in memory
- function updateData(uint256 newData) public {
- // Declare a memory variable to store the new data
- uint256 tempData = newData;
-
- // Assign the value of the memory variable to the storage variable
- storedData = tempData;
- }
-}
-```
-
-In the contract, we declare a memory variable called `tempData` and assign the input parameter `newData` to it to update its value. The `tempData` variable is then assigned to the `storedData` variable to update its value in storage.
-
-Unlike storage, data stored in memory is not persisted across transactions and is only accessible during the execution of the function. However, accessing and modifying data in memory is less expensive than doing so in storage, making it a more efficient option when dealing with temporary data. Additionally, any data stored in memory is not visible on the blockchain and cannot be read by external parties.
-
-#### Stack
-
-The stack is another form of temporary data storage, specifically used for holding function arguments, local variables, and intermediate values during function execution. The stack follows a Last-In-First-Out (LIFO) structure, meaning that the most recently added item is the first to be removed. This storage type is highly efficient but has limited space, making it suitable for small-scale data manipulation during function execution.
-
-The stack is an internal data structure used by the EVM (Ethereum Virtual Machine) for computation during the execution of transactions. When a transaction is executed by the EVM, the bytecode of the smart contract is loaded into memory, and the EVM uses the stack to keep track of intermediate results and execute operations.
-
-In Solidity, developers do not interact with the stack directly, but can optimize their code to make the best use of it and minimize the amount of gas used during transaction execution. This can include using more efficient algorithms or data structures, or avoiding unnecessary operations that can increase the depth of the stack.
-
-Key attributes of the stack:
-
-- **Temporary:** Like memory, stack data is only available during a single transaction execution and is lost afterward.
-
-- **Highly efficient:** Stack operations consume minimal gas, making it the most cost-effective storage option for small-scale data manipulation.
-
-- **LIFO structure:** The stack follows the Last-In-First-Out order, which allows for efficient management of function arguments, local variables, and intermediate values.
-
-- **Limited space:** The stack has a maximum depth of 1024, restricting the number of elements it can hold at a given time.
-
-- **Limited visibility:** Only the top 16 elements in the stack are accessible, limiting how many variables and other elements can be in scope at one time.
-
-Let's compare two versions of a function and analyze their gas efficiency with regard to stack usage and gas consumption:
-
-```solidity
-contract GasEfficiencyDemo {
- uint256 public result;
-
- // Less efficient
- function sumLessEfficient(uint256 a, uint256 b) public {
- uint256 temp = a + b;
- result = temp;
- }
-
- // More efficient
- function sumMoreEfficient(uint256 a, uint256 b) public {
- result = a + b;
- }
-}
-```
-
-In the `sumLessEfficient` function, the sum of the two input arguments `a` and `b` is first assigned to the temporary variable `temp` before being assigned to the state variable `result`. This additional step introduces an extra variable on the stack, which requires more gas for stack operations and consumes more gas overall.
-
-In contrast, the `sumMoreEfficient` function directly assigns the sum of the input arguments `a` and `b` to the state variable result. This eliminates the need for the temporary variable and reduces the stack usage, leading to lower gas consumption for stack operations and a more gas-efficient execution.
-
-Although the difference in gas consumption between these two functions may not be significant for such a simple example, the principle of minimizing stack usage and optimizing code to reduce gas consumption is essential for developing efficient smart contracts. By avoiding unnecessary variables and operations, you can improve the gas efficiency of your functions and reduce the cost of executing them on the EVM.
-
-## Variable Storage
-
-### Variable Packing
-
-As we've learned, minimizing the storage footprint of a contract can substantially reduce gas costs. To make storage more efficient, Ethereum employs a concept called variable packing.
-
-Variable packing is the process of placing multiple smaller variables into a single storage slot to optimize storage usage. A storage slot is a fixed-size container that can hold up to 32 bytes of data. Ethereum's Solidity compiler automatically packs smaller variables together if they can fit into a single storage slot.
-
-
-
-
-
-### Ordering Variable Declarations
-
-When declaring variables in a contract, their order can impact a contract's gas usage. You can optimize storage by declaring variables of similar sizes together, such that they can be packed into the same storage slot.
-
-Let's illustrate how this works:
-
-```solidity
-contract StoragePackingExample {
- uint8 a; // 1 byte
- uint8 b; // 1 byte
- uint256 c; // 32 bytes
-}
-```
-
-In this example, the compiler will automatically pack `a` and `b` into the same storage slot, as they are both 1-byte variables and can fit into a single 32-byte storage slot. However, `c` requires a separate storage slot due to its size (32 bytes).
-
-
-
-
-
-If these variables were not in the correct order, the contract would not take advantage of variable packing. The variables would take up more storage and would potentially consume more gas to execute the contract.
-
-Let's consider an inefficient example:
-
-```solidity
-contract StoragePackingBadExample {
- uint8 a; // 1 byte
- uint256 b; // 32 bytes
- uint8 c; // 1 byte
-}
-```
-
-In this contract, the variables are not declared in the optimal order, and the compiler would store these variables in the following way:
-
-
-
-
-
-To make the most of variable packing, it's important to group variables of the same size together and avoid mixing variable sizes. By doing this, the compiler can store them more efficiently, reducing the overall storage usage of the contract. This optimization will not only reduce the gas costs associated with storage, but it will also improve the contract's execution speed.
-
----
-
-## Conclusion
-
-Creating efficient and optimized smart contracts on Ethereum requires a thorough understanding of how storage works. Smart contracts use a key-value store model to manage and store data, which is simple, scalable, gas-efficient, and suitable for decentralized environments. There are three types of storage in Ethereum smart contracts: storage, memory, and stack, each with specific characteristics. Developers can optimize storage usage by using variable packing and ordering variable declarations based on their size. By following best practices for storage management, developers can create contracts that are lean, efficient, cost-effective, and improve their execution speed.
-
----
-
-## See Also
-
-- [Understanding Ethereum Smart Contract Storage](https://programtheblockchain.com/posts/2018/03/09/understanding-ethereum-smart-contract-storage/)
-- [What is Smart Contract Storage Layout](https://docs.alchemy.com/docs/smart-contract-storage-layout)
-
-
-[ethereum in depth, part 2]: https://blog.openzeppelin.com/ethereum-in-depth-part-2-6339cf6bddb9/
-[ethereum yellow paper]: https://ethereum.github.io/yellowpaper/paper.pdf
-
diff --git a/docs/learn/storage/simple-storage-sbs.mdx b/docs/learn/storage/simple-storage-sbs.mdx
deleted file mode 100644
index 32010af12..000000000
--- a/docs/learn/storage/simple-storage-sbs.mdx
+++ /dev/null
@@ -1,222 +0,0 @@
----
-title: Storing Data
-sidebarTitle: Step by Step Guide
-description: Learn how to Store data on the blockchain.
-hide_table_of_contents: false
----
-
-Ultimately, the power of the blockchain is that anyone can store their data on it via the `storage` in a smart contract. In this step-by-step guide, you'll learn how to access and use the `storage` data location.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Use the constructor to initialize a variable
-- Access the data in a public variable with the automatically generated getter
-- Order variable declarations to use storage efficiently
-
----
-
-## Simple Storage Contract
-
-Create a contract called `SimpleStorage`.
-
-### Add a Storage Variable
-
-In Solidity, variables declared at the class level are automatically `storage` variables. Create a variable to store the age of a person and another to store the number of cars that they own. Give `age` an initial value of your choosing, but don't make an assignment for `cars`;
-
-
-
-```solidity
-contract SimpleStorage {
- uint8 age = 41;
- uint8 cars;
-}
-```
-
-
-
-Because the age of a person, or the number of cars they own, is unlikely to be greater than 255, we can use a `uint8` for each of these. For types that are smaller than 32 bytes, multiple variables of the same type will be [packed] in the same storage slot. For this to work, the variables must be declared together.
-
-```solidity
-// These variables take advantage of packing
-uint8 first;
-uint8 second;
-uint third;
-
-// These variables DO NOT take advantage of packing and should be reordered
-uint8 fourth;
-uint fifth;
-uint8 sixth;
-```
-
-### Initializing a Value with the Constructor
-
-You may add a `constructor` function to your contract. Similar to other languages, this function is called exactly once, when the contract is deployed. The constructor may have parameters, but it does not require them.
-
-You can use the constructor to perform various setup tasks. For example, the constructor for the _ERC-721_ token that is the underlying mechanism for most NFTs uses the constructor to set up the name and symbol for the token.
-
-Create a constructor function and use it to assign the value of your choosing to `cars`.
-
-
-
-```solidity
-constructor() {
- cars = 1;
-}
-```
-
-
-
-### Accessing State Variables
-
-Deploy your contract in Remix. It should work fine, but you'll have one problem: there isn't a way to see if the variables have the expected values!
-
-You could solve this by writing functions that return the values in your state variables, but you don't need to. The Solidity compiler automatically creates getters for all `public` variables.
-
-Add the `public` keyword to both variables. Unlike most languages, `public` goes **after** the type declaration. Your contract should now be similar to:
-
-
-
-```solidity
-contract SimpleStorage {
- uint8 public age = 41;
- uint8 public cars;
- constructor() {
- cars = 1;
- }
-}
-```
-
-
-
-Redeploy your contract and test to confirm.
-
----
-
-## Setting a State Variable with a Function
-
-Good news! Our user bought a second car! The only problem is that we don't have a way to update the number of `cars` stored.
-
-### Add a Function to Update `cars`
-
-Before writing the function, let's think about design considerations for this feature. At any point in time, a user could:
-
-- Buy or otherwise acquire a new car
-- Get several new cars all at once (Woohoo!)
-- Sell or give away one or more cars (😞)
-
-Given this wide variety of conditions, **a** good approach would be to handle calculating the correct number of cars on the front end, and passing the updated value to the back end.
-
-To meet this need, we can write a `public` function that takes a `uint8` for `_numberOfCars` and then simply assigns that value to the state variable `cars`. Because this function modifies state, it **does not** need `pure` or `view`. It isn't either of those.
-
-
-
-```solidity
-function updateNumberOfCars(uint8 _numberOfCars) public {
- cars = _numberOfCars;
-}
-```
-
-
-
-Deploy and test to make sure it works as expected.
-
-
-While packing variables can save on gas costs, it can also increase them. The EVM operates on 32 bytes at a time, so it will take additional steps to reduce the size of the element for storage.
-
-Furthermore, the savings in writing to storage only apply when writing multiple values in the same slot at the same time.
-
-Review the **Warning** in the [layout] section of the docs for more details!
-
-
-
-### Add a Function to Update `age`
-
-It would also be good to be able to update the `age` value. This problem has slightly different considerations. Sadly, `age` will never go down. It should also probably only go up by one year for each update. The `++` operator works in Solidity, so we can use that to create a function that simply increments age when called.
-
-
-
-
-```solidity
-function increaseAge() public {
- age++;
-}
-```
-
-
-
-
-But what if a user calls this function by mistake? Good point!
-
-On your own, add a function called `adminSetAge` that can set the `age` to a specified value.
-
-### Refactor the Constructor to Accept Arguments
-
-We've got one problem remaining with this contract. What if your user has a different `age` or number of `cars` than what you've hardcoded into the contract?
-
-As mentioned above, the `constructor` **can** take arguments and use them during deployment. Let's refactor the contract to set the two state variables in the constructor based on provided values.
-
-
-
-```solidity
-contract SimpleStorage {
- uint8 public age;
- uint8 public cars;
- constructor(uint8 _age, uint8 _cars) {
- age = _age;
- cars = _cars;
- }
-}
-```
-
-
-
-Redeploy your contract. Note that now you have added parameters to the `constructor`, you'll have to provide them during deployment.
-
-
-
-
-
-Once completed, your contract should be similar to:
-
-
-
-
-```solidity
-contract SimpleStorage {
- uint8 public age;
- uint8 public cars;
- constructor(uint8 _age, uint8 _cars) {
- age = _age;
- cars = _cars;
- }
-
- function updateNumberOfCars(uint8 _numberOfCars) public {
- cars = _numberOfCars;
- }
-
- function increaseAge() public {
- age++;
- }
-
- function adminSetAge(uint8 _age) public {
- age = _age;
- }
-}
-```
-
-
-
----
-
-## Conclusion
-
-In this lesson, you've explored how to persistently store values on the blockchain. You've also practiced updating them from functions. Finally, you've learned how to use the constructor to perform setup functionality during deployment, with and without parameters.
-
----
-
-[packed]: https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html
-[layout]: https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html
diff --git a/docs/learn/storage/simple-storage-video.mdx b/docs/learn/storage/simple-storage-video.mdx
deleted file mode 100644
index ae6b828a4..000000000
--- a/docs/learn/storage/simple-storage-video.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Simple Storage
-description: Store data on the blockchain.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/storage/storage-exercise.mdx b/docs/learn/storage/storage-exercise.mdx
deleted file mode 100644
index a4f936197..000000000
--- a/docs/learn/storage/storage-exercise.mdx
+++ /dev/null
@@ -1,118 +0,0 @@
----
-title: Storage Exercise
-sidebarTitle: Exercise
-description: Exercise - Demonstrate your knowledge of storage.
-hide_table_of_contents: false
----
-
-import { Danger } from "/snippets/danger.mdx";
-
-Create a contract that adheres to the following specifications:
-
----
-
-## Contract
-
-Create a single contract called `EmployeeStorage`. It should not inherit from any other contracts. It should have the following functions:
-
-### State Variables
-
-The contract should have the following state variables, optimized to minimize storage:
-
-- A private variable `shares` storing the employee's number of shares owned
- - Employees with more than 5,000 shares count as directors and are stored in another contract
-- Public variable `name` which stores the employee's name
-- A private variable `salary` storing the employee's salary
- - Salaries range from 0 to 1,000,000 dollars
-- A public variable `idNumber` storing the employee's ID number
- - Employee numbers are not sequential, so this field should allow any number up to 2^256-1
-
-### Constructor
-
-When deploying the contract, utilize the `constructor` to set:
-
-- `shares`
-- `name`
-- `salary`
-- `idNumber`
-
-For the purposes of the test, you **must** deploy the contract with the following values:
-
-- `shares` - 1000
-- `name` - Pat
-- `salary` - 50000
-- `idNumber` - 112358132134
-
-### View Salary and View Shares
-
-
-In the world of blockchain, nothing is ever secret!\* `private` variables prevent other contracts from reading the value. You should use them as a part of clean programming practices, but marking a variable as private **does _not_ hide the value**. All data is trivially available to anyone who knows how to fetch data from the chain.
-
-\*You can make clever use of encryption though!
-
-
-
-Write a function called `viewSalary` that returns the value in `salary`.
-
-Write a function called `viewShares` that returns the value in `shares`.
-
-### Grant Shares
-
-Add a public function called `grantShares` that increases the number of shares allocated to an employee by `_newShares`. It should:
-
-- Add the provided number of shares to the `shares`
- - If this would result in more than 5000 shares, revert with a custom error called `TooManyShares` that returns the number of shares the employee would have with the new amount added
- - If the number of `_newShares` is greater than 5000, revert with a string message, "Too many shares"
-
-### Check for Packing and Debug Reset Shares
-
-Add the following function to your contract exactly as written below.
-
-```solidity
-/**
-* Do not modify this function. It is used to enable the unit test for this pin
-* to check whether or not you have configured your storage variables to make
-* use of packing.
-*
-* If you wish to cheat, simply modify this function to always return `0`
-* I'm not your boss ¯\_(ツ)_/¯
-*
-* Fair warning though, if you do cheat, it will be on the blockchain having been
-* deployed by your wallet....FOREVER!
-*/
-function checkForPacking(uint _slot) public view returns (uint r) {
- assembly {
- r := sload (_slot)
- }
-}
-
-/**
-* Warning: Anyone can use this function at any time!
-*/
-function debugResetShares() public {
- shares = 1000;
-}
-```
-
----
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/structs/structs-exercise.mdx b/docs/learn/structs/structs-exercise.mdx
deleted file mode 100644
index 097d6144f..000000000
--- a/docs/learn/structs/structs-exercise.mdx
+++ /dev/null
@@ -1,81 +0,0 @@
----
-title: Structs Exercise
-description: Exercise - Demonstrate your knowledge of structs.
-hide_table_of_contents: false
-sidebarTitle: Exercise
----
-
-Create a contract that adheres to the following specifications.
-
----
-
-## Contract
-
-Create a contract called `GarageManager`. Add the following in storage:
-
-- A public mapping called `garage` to store a list of `Car`s (described below), indexed by address
-
-Add the following types and functions.
-
-### Car Struct
-
-Implement a `struct` called `Car`. It should store the following properties:
-
-- `make`
-- `model`
-- `color`
-- `numberOfDoors`
-
-### Add Car Garage
-
-Add a function called `addCar` that adds a car to the user's collection in the `garage`. It should:
-
-- Use `msg.sender` to determine the owner
-- Accept arguments for make, model, color, and number of doors, and use those to create a new instance of `Car`
-- Add that `Car` to the `garage` under the user's address
-
-### Get All Cars for the Calling User
-
-Add a function called `getMyCars`. It should return an array with all of the cars owned by the calling user.
-
-### Get All Cars for Any User
-
-Add a function called `getUserCars`. It should return an array with all of the cars for any given `address`.
-
-### Update Car
-
-Add a function called `updateCar`. It should accept a `uint` for the index of the car to be updated, and arguments for all of the `Car` types.
-
-If the sender doesn't have a car at that index, it should revert with a custom `error` `BadCarIndex` and the index provided.
-
-Otherwise, it should update that entry to the new properties.
-
-### Reset My Garage
-
-Add a public function called `resetMyGarage`. It should delete the entry in `garage` for the sender.
-
----
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-{/* */}
-
-
-
-
-Ensure your variable sizes align with their intended use, and consider the nuances of packing in Solidity. Resources: [Solidity - Layout in Storage](https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#layout-of-state-variables-in-storage), [Variables in Struct](/learn/structs/structs-sbs#setting-up-the-struct)
-
diff --git a/docs/learn/structs/structs-sbs.mdx b/docs/learn/structs/structs-sbs.mdx
deleted file mode 100644
index 8dc92f267..000000000
--- a/docs/learn/structs/structs-sbs.mdx
+++ /dev/null
@@ -1,325 +0,0 @@
----
-title: Structs
-description: Practice using structs.
-hide_table_of_contents: false
-sidebarTitle: Step by Step Guide
----
-
-The `struct` type allows you to organize related data of different types.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Construct a `struct` (user-defined type) that contains several different data types
-- Declare members of the `struct` to maximize storage efficiency
-- Describe constraints related to the assignment of `struct`s depending on the types they contain
-
----
-
-## Creating a Struct
-
-In the last exercise, we used a `mapping` to create a relationship between an `address` and a `uint`. But what if your users have favorite colors too? Or favorite cars? You **could** create a `mapping` for each of these, but it would quickly get awkward. Instead, a [`struct`](https://docs.soliditylang.org/en/v0.8.17/types.html#structs) can be used to create a custom type that can store all of a user's favorites within one data type.
-
-Create a new contract called `Structs`.
-
-### Setting up the Struct
-
-Instantiate a `struct` with the keyword, followed by a name for the type, curly brackets, and the variables that make up the type. Add a stub for `Favorites`:
-
-```solidity
-struct Favorites {
-
-}
-```
-
-After consulting with the designers, we need to store the following for each address's favorites:
-
-- Favorite number
-- Birth Day of Month
-- Favorite color
-- Lucky Lottery numbers
-
-Let's pause for a moment and do some technical design around how to save our favorites.
-
-The product team has confirmed for us that we can safely expect that no users have a favorite number greater than 65,536, and of course, everyone is born on a day of the month between 1-31.
-
-Variable [packing](https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html) also works inside structs, so we could potentially save on storage by using smaller `uint`s for those variables. However, people don't change their favorite number very often, and the day of the month that they were born on never changes.
-
-Therefore, it's probably more gas-efficient and less cumbersome to write other parts of the code, if we just use `uint` for both variables.
-
-Favorite color can be a `string`.
-
-For Lucky Lottery Numbers, we need a collection. We could use a dynamic array, since this will be in _storage_, but we already know that the lottery has 5 numbers.
-
-Try to use this information to build the `struct` on your own. You should end up with something similar to:
-
-
-
-```solidity
-struct Favorites {
- uint favoriteNumber;
- uint birthDay;
- string favoriteColor;
- uint[5] lotteryNumbers;
-}
-```
-
-
-
-### Instantiating a Struct with Its Name
-
-There are two ways to instantiate a struct using its name. The first is similar to instantiating a new object in JavaScript:
-
-```solidity
-Favorites memory myFavorites = Favorites({
- favoriteNumber: 29,
- birthDay: 14,
- favoriteColor: "red",
- lotteryNumbers: [uint(1), 2, 3, 4, 5]
-});
-```
-
-You can also use a shorthand method where you skip the member names and just list a value for each one. Note that the curly brackets are **not** included in this format:
-
-```solidity
-Favorites memory myFavorites = Favorites(
- 29,
- 14,
- "red",
- [uint(1), 2, 3, 4, 5]
-);
-```
-
-There's no difference in gas costs with either of these methods. Use the one that makes the most sense for the given situation.
-
-### Saving Multiple Instances to Storage
-
-Next, we need to figure out the best way to organize the `Favorites` in _storage_. There are a few options, as always, each with tradeoffs. You could match the pattern you used for favorite numbers and utilize a `mapping` to match `addresses` to `Favorites`.
-
-Another popular method is to use an array, which takes advantage of `.push` returning a reference to the newly added element, and the fact that the concept of _undefined_ does not exist in Solidity.
-
-First, instantiate an array of `Favorites`:
-
-```solidity
-Favorites[] public userFavorites;
-```
-
-Next, add a `public` function to add submitted favorites to the list. It should take each of the members as an argument. Then, assign each argument to the new element via the reference returned by `push()`.
-
-
-
-```solidity
-function addFavorite(
- uint _favoriteNumber,
- uint _birthDay,
- string calldata _favoriteColor,
- uint[5] calldata _lotteryNumbers
-) public {
- // .push() returns a reference to the new element
- Favorites storage newFavorite = userFavorites.push();
- newFavorite.favoriteNumber = _favoriteNumber;
- newFavorite.birthDay = _birthDay;
- newFavorite.favoriteColor = _favoriteColor;
- newFavorite.lotteryNumbers = _lotteryNumbers;
-}
-```
-
-
-
-Alternatively, you can create an instance in memory, then `push` it to storage.
-
-
-
-```solidity
-function addFavorite(
- uint _favoriteNumber,
- uint _birthDay,
- string calldata _favoriteColor,
- uint[5] calldata _lotteryNumbers
-) public {
- Favorites memory myFavorites = Favorites(
- 29,
- 14,
- "red",
- [uint(1), 2, 3, 4, 5]
- );
-
- userFavorites.push(myFavorites);
-}
-```
-
-
-
-The gas cost is similar for each of these methods.
-
----
-
-## Unexpected Behavior in Structs
-
-Structs in Solidity exhibit some properties that are unexpected, or even frustrating. Working with them often includes untangling a set of mutually-exclusive properties and needs.
-
-### Dynamic Storage Arrays in Structs
-
-The product team has contacted you to let you know that the beta testers are complaining about the `lotteryNumbers`. As it turns out, not every locality has lotteries where 5 numbers are drawn. Some have 3, 4, or even 6!.
-
-You might think this is an easy enough change. After all, you can just remove the size from the array declaration inside `Favorites`. Go ahead and try it:
-
-```solidity
-struct Favorites {
- uint favoriteNumber;
- uint birthDay;
- string favoriteColor;
- uint[] lotteryNumbers; // Removed the '5'
-}
-```
-
-You'll get an error if you're using the `memory` method shown above.
-
-```text
-from solidity:
-TypeError: Invalid type for argument in function call. Invalid implicit conversion from uint256[5] memory to uint256[] memory requested.
- --> contracts/mappings_exercise.sol:70:13:
- |
-70 | [uint(1), 2, 3, 4, 5]
- | ^^^^^^^^^^^^^^^^^^^^^
-```
-
-The simplest resolution here is to switch back to using `push()` to create an empty instance of `Favorites`, then assigning the values.
-
-The reason this works is a little obtuse. In the failing example, an unsized `uint` array is the expected type for the argument, but a sized `uint` array is provided. Solidity cannot perform implicit conversions like this most of the time and you'll get a compiler error if you provide the wrong type for an argument, even if it is convertible.
-
-One exception to this rule is that Solidity **can** perform an implicit conversion during _assignment_ if the variable on the right side "fits" into the variable on the left side.
-
-`uint[5]` fits in `uint[]`, so Solidity will allow it to sit 🐈.
-
-But what happens if you use the _getter_ for `userFavorites` to retrieve your entry?
-
-```text
-{
- "0": "uint256: favoriteNumber 29",
- "1": "uint256: birthDay 14",
- "2": "string: favoriteColor red"
-}
-```
-
-What happened to the array? It's not there, and it turns out that this is [on purpose](https://github.com/ethereum/solidity/issues/1626).
-
-### Mappings Inside of Structs
-
-You may add `mappings` inside of `struct`s, subject to a few quirks and restrictions. Add `mapping (uint => uint) numberPairs;` to `Favorites`.
-
-In `addFavorites`, assign `newFavorite.numberPairs[33] = 66;`
-
-Deploy and test. So far, so good!
-
-_Déjà vu_ ahead: But what happens if you use the _getter_ for `userFavorites` to retrieve your entry?
-
-```text
-{
- "0": "uint256: favoriteNumber 29",
- "1": "uint256: birthDay 14",
- "2": "string: favoriteColor red"
-}
-```
-
-It's not there, and it turns out that this is [on purpose](https://github.com/ethereum/solidity/issues/1626).
-
-Another issue emerges if you try to return the struct from a public function. What if you wanted your `addFavorite` function to return a reference to the new favorite?
-
-```solidity
-// Bad code example, will not work
-function addFavorite(
- uint _favoriteNumber,
- uint _birthDay,
- string calldata _favoriteColor,
- uint[] calldata _lotteryNumbers
-) public returns (newFavorite memory) {
- // .push() returns a reference to the new element
- Favorites storage newFavorite = userFavorites.push();
- newFavorite.favoriteNumber = _favoriteNumber;
- newFavorite.birthDay = _birthDay;
- newFavorite.favoriteColor = _favoriteColor;
- newFavorite.lotteryNumbers = _lotteryNumbers;
- newFavorite.numberPairs[33] = 66;
-
- return newFavorite;
-}
-```
-
-You'll get an error. The `mapping` type cannot be returned by a `public` or `external` function, so neither can a `struct` that contains one.
-
-```text
-from solidity:
-TypeError: Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
- --> contracts/mappings_exercise.sol:64:23:
- |
-64 | ) public returns (Favorites memory) {
- | ^^^^^^^^^^^^^^^^
-```
-
-Finally, what happens if you try to assign `newFavorite` to a `memory` variable? Again, an error occurs because `mapping`s can only be in `storage`.
-
-```solidity
-// Bad code example, will not work
-Favorites memory secondFavorite = newFavorite;
-```
-
-```text
-from solidity:
-TypeError: Type struct Structs.Favorites memory is only valid in storage because it contains a (nested) mapping.
- --> contracts/mappings_exercise.sol:82:9:
- |
-82 | Favorites memory secondFavorite = newFavorite;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-```
-
-### Automatic Getters for Public Structs
-
-As with other types, if you put a `public` `struct` in storage at the contract level, the compiler will generate a getter automatically. However, these don't work quite the way you might expect. For example, imagine:
-
-```solidity
-
-struct MyStruct {
- uint first;
- uint second;
- uint third;
-}
-
-MyStruct myStruct;
-
-```
-
-The automatic getter for `myStruct` will **not** be:
-
-```solidity
-// Approximate example, not real code
-function myStruct() public view returns (MyStruct memory) {
- return myStruct;
-}
-```
-
-Instead, it returns the members individually:
-
-```solidity
-// Approximate example, not real code
-function myStruct() public view returns (uint, uint, uint) {
- return (myStruct.first, myStruct.second, myStruct.third);
-}
-```
-
-Create your own getter to return the data as a tuple, which will be interpreted as the appropriate type if it's called from another contract via an interface.
-
-```solidity
-function getMyStruct() public view returns (MyStruct memory) {
- return myStruct;
-}
-```
-
----
-
-## Conclusion
-
-In this lesson, you've learned how to use the `struct` keyword to create a custom type that stores related data. You've also learned three methods of instantiating them and common patterns for storing `struct`s in storage. Finally, you've explored some of the constraints that emerge when working with more complex data types within a `struct`.
diff --git a/docs/learn/structs/structs-vid.mdx b/docs/learn/structs/structs-vid.mdx
deleted file mode 100644
index cb39846e3..000000000
--- a/docs/learn/structs/structs-vid.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-title: Structs
-description: Create user-defined types.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/erc-20-token/analyzing-erc-20-vid.mdx b/docs/learn/token-development/erc-20-token/analyzing-erc-20-vid.mdx
deleted file mode 100644
index b7cd981d8..000000000
--- a/docs/learn/token-development/erc-20-token/analyzing-erc-20-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Analyzing the ERC-20 Token
-description: Explore the ERC-20 standard.
-sidebarTitle: Analyzing ERC-20
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/erc-20-token/erc-20-exercise.mdx b/docs/learn/token-development/erc-20-token/erc-20-exercise.mdx
deleted file mode 100644
index 9feedfbf3..000000000
--- a/docs/learn/token-development/erc-20-token/erc-20-exercise.mdx
+++ /dev/null
@@ -1,124 +0,0 @@
----
-title: ERC-20 Tokens Exercise
-description: Exercise - Create your own ERC-20 token!
-hide_table_of_contents: false
-sidebarTitle: Exercise
----
-
-Create a contract that adheres to the following specifications.
-
----
-
-## Contract
-
-Create a contract called `WeightedVoting`. Add the following:
-
-- A `maxSupply` of 1,000,000
-- Errors for:
- - `TokensClaimed`
- - `AllTokensClaimed`
- - `NoTokensHeld`
- - `QuorumTooHigh`, returning the quorum amount proposed
- - `AlreadyVoted`
- - `VotingClosed`
-- A struct called `Issue` containing:
- - An OpenZeppelin Enumerable Set storing addresses called `voters`
- - A string `issueDesc`
- - Storage for the number of `votesFor`, `votesAgainst`, `votesAbstain`, `totalVotes`, and `quorum`
- - Bools storing if the issue is `passed` and `closed`
-
-
-The unit tests require this `struct` to be constructed with the variables in the order above.
-
-
-
-- An array of `Issue`s called `issues`
-- An `enum` for `Vote` containing:
- - `AGAINST`
- - `FOR`
- - `ABSTAIN`
-- Anything else needed to complete the tasks
-
-Add the following functions.
-
-### Constructor
-
-Initialize the ERC-20 token and burn the zeroeth element of `issues`.
-
-### Claim
-
-Add a `public` function called `claim`. When called, so long as a number of tokens equalling the `maximumSupply` have not yet been distributed, any wallet _that has not made a claim previously_ should be able to claim 100 tokens. If a wallet tries to claim a second time, it should revert with `TokensClaimed`.
-
-Once all tokens have been claimed, this function should revert with an error `AllTokensClaimed`.
-
-
-In our simple token, we used `totalSupply` to mint our tokens up front. The ERC20 implementation we're using also tracks `totalSupply`, but does it differently.
-
-Review the docs and code comments to learn how.
-
-
-
-### Create Issue
-
-Implement an `external` function called `createIssue`. It should add a new `Issue` to `issues`, allowing the user to set the description of the issue, and `quorum` - which is how many votes are needed to close the issue.
-
-Only token holders are allowed to create issues, and issues cannot be created that require a `quorum` greater than the current total number of tokens.
-
-This function must return the index of the newly-created issue.
-
-
-One of the unit tests will break if you place your check for `quorum` before the check that the user holds a token. The test compares encoded error names, which are **not** human-readable. If you are getting `-> AssertionError: �s is not equal to �9�` or similar, this is likely the issue.
-
-
-
-### Get Issue
-
-Add an `external` function called `getIssue` that can return all of the data for the issue of the provided `_id`.
-
-`EnumerableSet` has a `mapping` underneath, so it can't be returned outside of the contract. You'll have to figure something else out.
-
-
-**Hint**
-
-The return type for this function should be a `struct` very similar to the one that stores the issues.
-
-
-### Vote
-
-Add a `public` function called `vote` that accepts an `_issueId` and the token holder's vote. The function should revert if the issue is closed, or the wallet has already voted on this issue.
-
-Holders must vote all of their tokens for, against, or abstaining from the issue. This amount should be added to the appropriate member of the issue and the total number of votes collected.
-
-If this vote takes the total number of votes to or above the `quorum` for that vote, then:
-
-- The issue should be set so that `closed` is true
-- If there are **more** votes for than against, set `passed` to `true`
-
----
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-
-The contract specification contains actions that can only be performed once by a given address. As a result, the unit tests for a passing contract will only be successful the **first** time you test.
-
-**You may need to submit a fresh deployment to pass**
-
-
-{/* */}
-
-
diff --git a/docs/learn/token-development/erc-20-token/erc-20-standard.mdx b/docs/learn/token-development/erc-20-token/erc-20-standard.mdx
deleted file mode 100644
index 9ef862814..000000000
--- a/docs/learn/token-development/erc-20-token/erc-20-standard.mdx
+++ /dev/null
@@ -1,103 +0,0 @@
----
-title: The ERC-20 Token Standard
-description: An overview of the ERC-20 token standard
-sidebarTitle: ERC-20 Standard
-hide_table_of_contents: false
----
-
-
-In this article, we'll delve into the structure and specifications of ERC-20 tokens, uncovering the key elements that contribute to their widespread adoption and diverse use cases.
-
----
-
-## Objectives:
-
-By the end of this lesson you should be able to:
-
-- Analyze the anatomy of an ERC-20 token
-- Review the formal specification for ERC-20
-
----
-
-## Introduction
-
-The emergence of the ERC-20 token standard marked a significant milestone in the evolution of the Ethereum ecosystem, providing a unified and adaptable framework for creating and managing fungible tokens. As the backbone for a vast array of applications in decentralized finance and beyond, ERC-20 tokens facilitate seamless collaboration and interoperability within the Ethereum ecosystem. Their adaptable nature and standardized structure have made them the go-to choice for developers and users alike, laying the groundwork for continued innovation and growth in the Ethereum space.
-
-The ERC-20 token standard has not only streamlined the creation of new tokens but also bolstered the overall user experience by establishing a consistent set of rules and functions. As a result, it has garnered widespread adoption and solidified its position as the de facto standard for fungible tokens on Ethereum, driving the expansion of the decentralized economy and fostering the development of novel applications and services.
-
-
-
-
-
----
-
-## ERC-20 Specification
-
-EIP-20 (Ethereum Improvement Proposal 20) is the formal specification for ERC-20, defining the requirements to create compliant tokens on the Ethereum blockchain. EIP-20 prescribes the mandatory functions, optional functions, and events a token must implement to achieve ERC-20 compliance. Adherence to EIP-20 allows developers to create tokens compatible with existing Ethereum applications and services, streamlining integration.
-
----
-
-## Anatomy of an ERC-20 Token
-
-An ERC-20 token consists of a smart contract that implements the standardized interface, which comprises a set of six mandatory functions:
-
-- **totalSupply():** Returns the total supply of the token.
-- **balanceOf(address):** Provides the balance of tokens held by a specific address.
-- **transfer(address, uint256):** Transfers a specified amount of tokens from the sender's address to the specified recipient's address.
-- **transferFrom(address, address, uint256):** Enables a third party to transfer tokens on behalf of the token owner, given that the owner has approved the transaction.
-- **approve(address, uint256):** Allows the token owner to grant permission to a third party to spend a specified amount of tokens on their behalf.
-- **allowance(address, address):** Returns the amount of tokens the token owner has allowed a third party to spend on their behalf.
-
-Additionally, ERC-20 tokens can include optional functions that provide descriptive information about the token:
-
-- **name():** Returns the name of the token, for example, "Uniswap."
-- **symbol():** Provides the token's symbol, like "UNI."
-- **decimals():** Indicates the number of decimal places the token can be divided into, typically 18 for most tokens.
-
----
-
-## Benefits of ERC-20 Standardization
-
-The standardization of ERC-20 tokens provides several benefits for both developers and users. For developers, it simplifies the process of creating new tokens by providing a consistent set of functions and conventions. This reduces the likelihood of errors and ensures a smooth integration with existing applications and services in the Ethereum ecosystem.
-
-
-
-
-
-For users, the standardized interface makes it easier to interact with a wide variety of tokens, regardless of their specific purpose or implementation. This means that users can effortlessly check their token balance, transfer tokens, or approve transactions using the same set of functions, whether they are interacting with a governance token like UNI or a stablecoin like USDC.
-
-
-
-
-
----
-
-## Applications
-
-ERC-20 tokens find wide-ranging applications in various categories, each with its unique purpose and functionality:
-
-- **Utility tokens:** These tokens can be used to access specific services or features within a platform. Examples include Filecoin (FIL) for decentralized storage, Basic Attention Token (BAT) for digital advertising, and Decentraland's MANA for purchasing virtual land and assets.
-
-- **Governance tokens:** These tokens grant voting rights and influence over the development of a project, allowing holders to participate in decision-making processes. Examples include Uniswap (UNI), Aave (AAVE), and Compound (COMP).
-
-- **Stablecoins:** These tokens maintain a relatively stable value pegged to a reserve of assets or fiat currency, providing a less volatile option for transactions and trading. Examples include USD Coin (USDC), Tether (USDT), and MakerDAO's DAI.
-
-- **Liquidity tokens:** Liquidity providers on DeFi platforms often receive ERC-20 tokens as a representation of their share in a liquidity pool. These tokens can be staked or traded, and they enable users to earn rewards for providing liquidity. Examples include Uniswap LP tokens and Curve LP tokens.
-
-- **Rewards tokens:** Some platforms issue ERC-20 tokens as incentives for users to participate in their ecosystem, such as staking, lending, or providing liquidity. These tokens can be earned as passive income or used to access additional platform features. Examples include Synthetix (SNX) and SushiSwap (SUSHI).
-
-Each of these use cases demonstrates the adaptability of ERC-20 tokens to serve different needs within the blockchain ecosystem.
-
----
-
-## Conclusion
-
-By providing a consistent framework for fungible tokens and adhering to the formal EIP-20 specification, ERC-20 has enabled the development of countless projects and applications that have revolutionized how value is exchanged and managed on Ethereum. Analyzing the anatomy of an ERC-20 token and reviewing its formal specification reveal the versatility and importance of this token standard.
-
----
-
-## See Also
-
-- [Introduction to Ethereum Improvement Proposals (EIPs)](https://ethereum.org/en/eips/)
-- [EIP-20: ERC-20 Token Standard](https://eips.ethereum.org/EIPS/eip-20)
-- [ERC-20 Token Standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/)
diff --git a/docs/learn/token-development/erc-20-token/erc-20-testing-vid.mdx b/docs/learn/token-development/erc-20-token/erc-20-testing-vid.mdx
deleted file mode 100644
index 4214a1c6f..000000000
--- a/docs/learn/token-development/erc-20-token/erc-20-testing-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: ERC-20 Testing
-description: Test the OpenZeppelin ERC-20 implementation.
-hide_table_of_contents: false
-sidebarTitle: Testing ERC-20
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/erc-20-token/erc-20-token-sbs.mdx b/docs/learn/token-development/erc-20-token/erc-20-token-sbs.mdx
deleted file mode 100644
index 34b9c8150..000000000
--- a/docs/learn/token-development/erc-20-token/erc-20-token-sbs.mdx
+++ /dev/null
@@ -1,141 +0,0 @@
----
-title: ERC-20 Implementation
-sidebarTitle: Step by Step Guide
-description: Implement your own ERC-20 token.
-hide_table_of_contents: false
----
-
-The ERC-20 is a standard that allows for the development of fungible tokens and helps sites and apps, such as exchanges, know how to find and display information about these tokens. You can leverage existing implementations, such as the one by [OpenZeppelin] to develop your own tokens.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Describe OpenZeppelin
-- Import the OpenZeppelin ERC-20 implementation
-- Describe the difference between the ERC-20 standard and OpenZeppelin's ERC20.sol
-- Build and deploy an ERC-20 compliant token
-
----
-
-## Setting Up the Contract
-
-Create a new Solidity file, add the license and pragma, and import the ERC-20 implementation linked above.
-
-Add a contract called `MyERC20Token` that inherits from `ERC20`.
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
-
-contract MyERC20Token is ERC20 {
-
-}
-```
-
-### Adding a Constructor
-
-Review the constructor on line 53 of the [OpenZeppelin] implementation. It requires strings for the name and symbol you wish to use for your token. They're using a slightly different naming convention by putting the `_` after the name of the parameters. Like any other function, you can pass variables of **any** name as long as they're the right type, so feel free to continue adding the `_` in front in your contract's constructor:
-
-```solidity
-constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
-
-}
-```
-
-
-There is neither a governing body nor built-in programmatic rules preventing you, or anyone else, from using the same name and symbol as an already in-use token. Scammers often take advantage of this fact, and even well-meaning developers can cause confusion by not being careful here.
-
-
-
-That's it. You're done! Deploy and test, and you should see all of the functionality called for by the standard and provided by the OpenZeppelin implementation.
-
-
-
-
-
-Do some testing. You'll see that the `totalSupply` and all balances are zero.
-
-By default, the decimal for the token will be 18, which is the most common choice. Remember, there aren't decimal types yet, so 1.0 ETH is really a `uint` holding 1 \* 10\*\*18, or 1000000000000000000.
-
----
-
-## ERC-20 Further Testing
-
-Line 251 of the [OpenZeppelin] implementation contains a `_mint` function, but it's internal. As a result, you'll need to figure out a minting mechanism and add it via your own contract.
-
-### Minting in the Constructor
-
-One method of using the `_mint` function is to create an initial supply of tokens in the constructor. Add a call to `_mint` that awards 1 full token to the contract creator. Remember, the decimal is 18. Minting literally `1` is creating a tiny speck of dust.
-
-```solidity
-constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
- _mint(msg.sender, 1 * 10**18);
-}
-```
-
-Redeploy. Without you needing to do anything, you should find that the `totalSupply` is now 1000000000000000000, as is the `balanceOf` the deploying address.
-
-You can also use this to mint to other users. Go ahead and add the second and third accounts:
-
-
-
-```solidity
-constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
- _mint(msg.sender, 1 * 10**18);
- _mint(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2, 1 * 10**18);
- _mint(0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db, 1 * 10**18);
-}
-```
-
-
-
-
-**Switch back** to the first account and redeploy. Test to confirm that each account has the appropriate amount of tokens.
-
-### Testing the Transfer Function
-
-Try using the `transfer` function to move tokens around.
-
-What happens if you try to burn a token by sending it to the zero address? Give it a try!
-
-You'll get an error, because protecting from burning is built into the `_transfer` function.
-
-```text
-transact to MyERC20Token.transfer pending ...
-transact to MyERC20Token.transfer errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-Reason provided by the contract: "ERC20: transfer to the zero address".
-Debug the transaction to get more information.
-```
-
-### Testing the Transfer From Function
-
-You might have noticed that there's another function called `transferFrom`. What's that for? Check the documentation in the contract to find out!
-
-This function works with the `allowance` function to give the owner of one wallet permission to spend up to a specified amount of tokens owned by another. Exchanges can make use of this to allow a user to post tokens for sale at a given price without needing to take possession of them.
-
----
-
-## ERC-20 Final Thoughts
-
-The world is still figuring out how to handle all of the new possibilities tokens provide. Old laws are being applied in new ways, and new laws are being written. Different jurisdictions are doing this in unique and sometimes conflicting ways.
-
-You should consult with a lawyer in your jurisdiction before releasing your own tokens.
-
----
-
-## Conclusion
-
-In this lesson, you've learned how easy it is to create an ERC-20 compliant token by using the OpenZeppelin implementation. You've reviewed at least one method to mint an initial supply of tokens, and that it's up to you to figure out the best way to create your tokens and follow all relevant laws and regulations.
-
----
-
-[OpenZeppelin]: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
diff --git a/docs/learn/token-development/erc-20-token/openzeppelin-erc-20-vid.mdx b/docs/learn/token-development/erc-20-token/openzeppelin-erc-20-vid.mdx
deleted file mode 100644
index baf1e8175..000000000
--- a/docs/learn/token-development/erc-20-token/openzeppelin-erc-20-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: OpenZeppelin ERC-20 Implementation
-description: Review a popular implementation of the ERC-20 standard.
-hide_table_of_contents: false
-sidebarTitle: OpenZeppelin ERC-20
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/erc-721-token/erc-721-exercise.mdx b/docs/learn/token-development/erc-721-token/erc-721-exercise.mdx
deleted file mode 100644
index dd6a5e119..000000000
--- a/docs/learn/token-development/erc-721-token/erc-721-exercise.mdx
+++ /dev/null
@@ -1,87 +0,0 @@
----
-title: ERC-721 Tokens Exercise
-description: Exercise - Create your own NFT!
-sidebarTitle: Exercise
-hide_table_of_contents: false
----
-
-import { Danger } from "/snippets/danger.mdx";
-
-Create a contract that adheres to the following specifications.
-
----
-
-## Contract
-
-Create a contract called `HaikuNFT`. Add the following to the contract:
-
-- A `struct` called `Haiku` to store the `address` of the `author` and `line1`, `line2`, and `line3`
-- A public array to store these `haikus`
-- A public `mapping` to relate `sharedHaikus` from the `address` of the wallet shared with, to the id of the Haiku NFT shared
-- A public `counter` to use as the id and to track and share the total number of Haikus minted
- - If 10 Haikus have been minted, the counter should be at 11, to serve as the next id
- - Do **NOT** assign an id of 0 to a haiku
-- Other variables as necessary to complete the task
-
-Add the following functions.
-
-### Constructor
-
-As appropriate.
-
-### Mint Haiku
-
-Add an `external` function called `mintHaiku` that takes in the three lines of the poem. This function should mint an NFT for the minter and save their Haiku.
-
-Haikus must be **unique**! If any line in the Haiku has been used as any line of a previous Haiku, revert with `HaikuNotUnique()`.
-
-You **don't** have to count syllables, but it would be neat if you did! (No promises on whether or not we counted the same as you did)
-
-### Share Haiku
-
-Add a `public` function called `shareHaiku` that allows the owner of a Haiku NFT to share that Haiku with the designated `address` they are sending it `_to`. Doing so should add it to that address's entry in `sharedHaikus`.
-
-If the sender isn't the owner of the Haiku, instead revert with an error of `NotYourHaiku`. Include the id of the Haiku in the error.
-
-
-Remember, everything on the blockchain is public. This sharing functionality can be expanded for features similar to allowing an app user to display the selected shared haiku on their profile.
-
-It does nothing to prevent anyone and everyone from seeing or copy/pasting the haiku!
-
-
-
-### Get Your Shared Haikus
-
-Add a `public` function called `getMySharedHaikus`. When called, it should return an array containing all of the haikus shared with the caller.
-
-If there are no haikus shared with the caller's wallet, it should revert with a custom error of `NoHaikusShared`, with no arguments.
-
----
-
-
-The contract specification contains actions that can only be performed once by a given address. As a result, the unit tests for a passing contract will only be successful the **first** time you test.
-
-**You may need to submit a fresh deployment to pass**
-
-
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](../deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/token-development/erc-721-token/erc-721-on-opensea-vid.mdx b/docs/learn/token-development/erc-721-token/erc-721-on-opensea-vid.mdx
deleted file mode 100644
index 6fc973036..000000000
--- a/docs/learn/token-development/erc-721-token/erc-721-on-opensea-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: ERC-721 Token On Opensea
-description: Learn how a popular marketplace interprets tokens.
-hide_table_of_contents: false
-sidebarTitle: OpenSea Integration
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/erc-721-token/erc-721-sbs.mdx b/docs/learn/token-development/erc-721-token/erc-721-sbs.mdx
deleted file mode 100644
index 4261f5988..000000000
--- a/docs/learn/token-development/erc-721-token/erc-721-sbs.mdx
+++ /dev/null
@@ -1,296 +0,0 @@
----
-title: ERC-721 Token
-description: Build your own NFT based on the ERC-721 standard.
-hide_table_of_contents: false
-sidebarTitle: Step by Step Guide
----
-
-import { Danger } from "/snippets/danger.mdx";
-
-Punks, Apes, and birds of all kinds. You've heard about them, seen them, and may even be lucky enough to own a famous NFT. Or maybe you've just bought into a random collection and aren't sure what to do with your NFT. NFTs aren't really pictures, or anything else specific. They're a method of proving ownership of a digital asset. Anyone can right-click on a picture of a monkey and set it as their profile picture, but only the owner can use it with apps that utilize web3 ownership.
-
-The ERC-721 token standard is the underlying technical specification that not only makes digital ownership possible, it provides a standardized way for marketplaces, galleries, and other sites to know how to interact with these digital items.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Analyze the anatomy of an ERC-721 token
-- Compare and contrast the technical specifications of ERC-20 and ERC-721
-- Review the formal specification for ERC-721
-- Build and deploy an ERC-721 compliant token
-- Use an ERC-721 token to control ownership of another data structure
-
----
-
-## Implementing the OpenZeppelin ERC-721 Token
-
-JPGs may be all the rage right now but in the future, the selfie you post on social media, a text message you send to your mother, and the +4 battleaxe you wield in your favorite MMO might all be NFTs.
-
-### Import and Setup
-
-Start by opening the [OpenZeppelin] ERC-721 in Github. Copy the link and use it to import the ERC-721 contract. Create your own contract, called `MyERC721`, that inherits from `ERC721Token`. Add a constructor that initializes the `_name` and `_symbol`.
-
-
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.17;
-
-import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
-
-contract MyERC721Token is ERC721 {
- constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {
-
- }
-}
-```
-
-
-### Minting NFTs
-
-The minting function that is provided by OpenZeppelin, `_safeMint`, is `internal`. To use it to let your customers mint NFTs, you'll need to implement a function in your contract that calls the one in the imported contract.
-
-Before you can do that, you need a way to supply the two parameters needed for `_safeMint`:
-
-- `address to` - the owner of the new NFT
-- `uint256 tokenId` - the ID number for the new NFT
-
-The owner is easy, you can simply use `msg.sender` to grant ownership to the wallet doing the minting.
-
-ID is slightly more challenging. A common practice is to simply assign the total number of NFTs, including the one being minted, as the `tokenId`. Doing so is straightforward, makes it easier to find all of the NFTs within a collection, and helps lean in to the common community perception that lower-number NFTs are better, just like other limited-edition collectibles.
-
-
-Obfuscating certain information, such as customer IDs, is often considered a best practice. Doing so might make it harder for an attacker who has circumvented other security functions from getting access to more data. If `134` is a valid `customer_id`, it is likely that `135` is too. The same can't be said for `bfcb51bd-c04f-42d5-8116-3def754e8c32`.
-
-This practice is not as useful on the blockchain, because all information is public.
-
-
-
-To implement ID generation, simply add a `uint` called `counter` to storage and initialize it as 1, either at declaration or in the constructor.
-
-Now, you can add a function called `redeemNFT` that calls `safeMint` using the `msg.sender` and `counter`, and then increments the `counter`:
-
-
-
-```solidity
-function redeemNFT() external {
- _safeMint(msg.sender, counter);
- counter++;
-}
-```
-
-
-
-
-As a programmer, you've probably gone through great pains to internalize the idea of zero-indexing. Arrays start at 0. The pixel in the top-left corner of your screen is located at 0, 0.
-
-As a result, you need to be very careful when working with Solidity because there isn't the concept of `undefined`, and "deleted" values return to their default value, which is 0 for numbers.
-
-To prevent security risks, you'll need to make sure that you never give an ID or array index of 0 to anything. Otherwise, attempting to delete a value, such as a `struct` member called `authorizedSellerID` might give the wallet address stored at index 0 access to that resource.
-
-
-
-Deploy and test. Be sure to:
-
-- Mint several NFTs
-- Transfer an NFT from one Remix account to another
-- Try to transfer an NFT to `0x0000000000000000000000000000000000000000`
-
----
-
-## ERC-721 URIs
-
-The ERC-721 standard includes the option to define a [URI] associated with each NFT. These are intended to point to a `json` file following the _ERC721 Metadata JSON Schema_
-
-```json
-{
- "title": "Asset Metadata",
- "type": "object",
- "properties": {
- "name": {
- "type": "string",
- "description": "Identifies the asset to which this NFT represents"
- },
- "description": {
- "type": "string",
- "description": "Describes the asset to which this NFT represents"
- },
- "image": {
- "type": "string",
- "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
- }
- }
-}
-```
-
-Note that they don't have to. In the OpenZeppelin implementation, the function that returns the `_baseURI` is `virtual` and must be overridden by an inheriting contract.
-
-```
-// OpenZeppelin ERC-721
-/**
- * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
- * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
- * by default, can be overridden in child contracts.
- */
-function _baseURI() internal view virtual returns (string memory) {
- return "";
-}
-```
-
-The owner of the contract can therefore choose what the value is and when, how, or if it is changeable. For example, the [Bored Ape Yacht Club] contract has a function allowing the owner to set or change the \_baseURI, changing where the metadata is stored, and potentially what is in it.
-
-```solidity
-// From boredapeyachtclub.sol
-function setBaseURI(string memory baseURI) public onlyOwner {
- _setBaseURI(baseURI);
-}
-```
-
-The metadata for [BAYC] is [stored on IPFS], but some projects even use centralized, web2 storage options!
-
-### NFT Switcheroo
-
-[Doodles] is another NFT collection that [uses IPFS] to store metadata. Let's modify our contract to swap metadata back and forth from one collection to the other.
-
-Start by saving the IPFS metadata bases as constants, at the contract level. Add an enum to enable selection between these two choices, and an instance of that enum.
-
-
-
-```solidity
- string constant BAYC = "https://ipfs.io/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/";
- string constant DOODLES = "https://ipfs.io/ipfs/QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/";
-
- enum NFTMetadata { BAYC, DOODLES }
- NFTMetadata nftMetadata = NFTMetadata.BAYC;
-```
-
-
-
-Finally, add an override of `_baseURI` that returns the appropriate selection based on which collection is active, and a function to swap the URI.
-
-
-
-```solidity
-function _baseURI() internal override view returns(string memory) {
- if (nftMetadata == NFTMetadata.BAYC) {
- return BAYC;
- } else if (nftMetadata == NFTMetadata.DOODLES){
- return DOODLES;
- } else {
- revert("Error...");
- }
-}
-
-function switchURI() public {
- // TODO: Limit to contract owner
- nftMetadata = nftMetadata == NFTMetadata.BAYC ? NFTMetadata.DOODLES : NFTMetadata.BAYC;
-}
-```
-
-
-
-Deploy, mint some NFTs, and call `tokenURI` to find the information for token number 1. You should get:
-
-```text
-https://ipfs.io/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/1
-```
-
-This links to the metadata json file for the first Bored Ape:
-
-```json
-{
- "image": "ipfs://QmPbxeGcXhYQQNgsC6a36dDyYUcHgMLnGKnF8pVFmGsvqi",
- "attributes": [
- {
- "trait_type": "Mouth",
- "value": "Grin"
- },
- {
- "trait_type": "Clothes",
- "value": "Vietnam Jacket"
- },
- {
- "trait_type": "Background",
- "value": "Orange"
- },
- {
- "trait_type": "Eyes",
- "value": "Blue Beams"
- },
- {
- "trait_type": "Fur",
- "value": "Robot"
- }
- ]
-}
-```
-
-IPFS links don't work natively directly in the browser, but you can see the image here:
-
-https://ipfs.io/ipfs/QmPbxeGcXhYQQNgsC6a36dDyYUcHgMLnGKnF8pVFmGsvqi/
-
-Now, call your `switchURI` function and then call `tokenURI` again for token 1.
-
-Now, you'll get a new link for metadata:
-
-```text
-https://ipfs.io/ipfs/QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1
-```
-
-Which contains the metadata for Doodle 1 instead of BAYC 1:
-
-```json
-{
- "image": "ipfs://QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
- "name": "Doodle #1",
- "description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian\u2013based illustrator, designer, animator and muralist.",
- "attributes": [
- {
- "trait_type": "face",
- "value": "holographic beard"
- },
- {
- "trait_type": "hair",
- "value": "white bucket cap"
- },
- {
- "trait_type": "body",
- "value": "purple sweater with satchel"
- },
- {
- "trait_type": "background",
- "value": "grey"
- },
- {
- "trait_type": "head",
- "value": "gradient 2"
- }
- ]
-}
-```
-
-Your robot ape is now a person with a rainbow beard!
-
-https://ipfs.io/ipfs/QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9
-
----
-
-## Conclusion
-
-In this lesson, you've learned how to use OpenZeppelin's ERC-721 implementation to create your own NFT contract. You've also learned how NFT metadata is stored, and that it is not necessarily immutable.
-
----
-
-[OpenZeppelin]: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol
-[Coinbase NFT]: https://nft.coinbase.com/
-[URI]: https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
-[stored on IPFS]: https://ipfs.io/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/
-[BAYC]: https://nft.coinbase.com/collection/ethereum/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
-[CryptoPunks]: https://nft.coinbase.com/collection/ethereum/0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb
-[Doodles]: https://nft.coinbase.com/collection/ethereum/0x8a90cab2b38dba80c64b7734e58ee1db38b8992e
-[uses IPFS]: https://ipfs.io/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/
-
diff --git a/docs/learn/token-development/erc-721-token/erc-721-standard-video.mdx b/docs/learn/token-development/erc-721-token/erc-721-standard-video.mdx
deleted file mode 100644
index 8f8e36175..000000000
--- a/docs/learn/token-development/erc-721-token/erc-721-standard-video.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: ERC-721 Token Standard
-description: Review the formal standard for the ERC-721 Token.
-hide_table_of_contents: false
-sidebarTitle: ERC-721 Standard
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
-
diff --git a/docs/learn/token-development/erc-721-token/erc-721-standard.mdx b/docs/learn/token-development/erc-721-token/erc-721-standard.mdx
deleted file mode 100644
index aaebe27cb..000000000
--- a/docs/learn/token-development/erc-721-token/erc-721-standard.mdx
+++ /dev/null
@@ -1,101 +0,0 @@
----
-title: The ERC-721 Token Standard
-description: An overview of the ERC-721 token standard
-hide_table_of_contents: false
-sidebarTitle: Standard Overview
----
-
-In this article, we'll delve into the ERC-721 token standard, exploring its technical specs, applications, and how it differs from the ERC-20 standard.
-
----
-
-## Objectives:
-
-By the end of this lesson you should be able to:
-
-- Analyze the anatomy of an ERC-721 token
-- Compare and contrast the technical specifications of ERC-20 and ERC-721
-- Review the formal specification for ERC-721
-
----
-
-## Introduction
-
-The development of the Ethereum ecosystem has been marked by key milestones, two of which are the inception of the ERC-20 and ERC-721 token standards. While ERC-20 provided a foundational framework for fungible tokens, ERC-721 established a flexible and adaptable infrastructure for non-fungible tokens (NFTs).
-
-The ERC-721 token standard is pivotal in the Ethereum ecosystem for creating and managing unique digital assets. With its consistent rules and functions, it has greatly enhanced the user experience, solidifying its position as the go-to standard for non-fungible tokens. ERC-721 has been instrumental in expanding the digital collectibles market and spurring the development of new applications and services.
-
-
-
-
-
----
-
-## ERC-721 Specification
-
-EIP-721 (Ethereum Improvement Proposal 721) is the formal specification for ERC-721, defining the requirements for creating compliant non-fungible tokens on Ethereum. EIP-721 prescribes mandatory functions and events that a token must implement to achieve ERC-721 compliance. Adherence to EIP-721 ensures compatibility of unique tokens with existing Ethereum applications and services, simplifying integration.
-
----
-
-## Anatomy of an ERC-721 Token
-
-An ERC-721 token comprises a smart contract implementing the standardized interface, which includes six primary functions:
-
-- **balanceOf(address)** Returns the number of tokens held by a specific address.
-- **ownerOf(uint256):** Provides the owner of a specified token.
-- **safeTransferFrom(address, address, uint256):** Transfers a specific token's ownership from one address to another.
-- **transferFrom(address, address, uint256):** Allows a third party to transfer tokens on the token owner's behalf, given the owner's approval.
-- **approve(address, uint256):** Enables the token owner to permit a third party to transfer a specific token on their behalf.
-- **getApproved(uint256):** Shows the approved address for a specific token.
-
-These functions ensure each ERC-721 token has a unique identifier and can be owned and transferred individually.
-
----
-
-## ERC-721 Vs ERC-20
-
-The ERC-721 and ERC-20 token standards share a common goal of providing a set of standards for tokens on the Ethereum network but diverge in terms of functionality and use cases.
-
-ERC-20 tokens are fungible, meaning each token is identical to every other token; they are interchangeable like currency. On the other hand, ERC-721 tokens are non-fungible, meaning each token is unique and not interchangeable with any other token. This uniqueness is made possible through the ownerOf() and getApproved() functions, which provide information about the ownership of each unique token.
-
-The ERC-20 standard has primarily found use in creating cryptocurrencies for apps, governance tokens, utility tokens, stablecoins, and more. The ERC-721 standard, conversely, has been adopted largely for creating unique digital assets like collectibles, digital art, and tokenized virtual real estate, among other applications.
-
----
-
-## Benefits of ERC-721 Standardization
-
-Standardizing non-fungible tokens via the ERC-721 token standard presents substantial benefits to developers and users in the Ethereum ecosystem. Developers have access to a standardized set of functions, leading to less code ambiguity, fewer errors, and a streamlined development process. This uniformity also ensures smooth integration with existing apps and platforms on Ethereum.
-
-For users, the ERC-721 standard offers an intuitive, consistent interface for interacting with a wide array of unique tokens. Regardless of the token's specific use or design, users can reliably check their ownership of tokens, transfer tokens to other addresses, and approve transactions. This consistency enhances usability across the Ethereum platform, from digital art marketplaces to tokenized real estate and gaming applications.
-
-
-
-
-
----
-
-## Applications
-
-ERC-721 tokens find wide-ranging applications in various categories:
-
-- **Digital Art:** Artists can create unique digital artworks as ERC-721 tokens. These tokens can be sold or traded on platforms like OpenSea, Rarible, and Coinbase NFT. Examples include work by the digital artist Beeple.
-
-- **Gaming:** Game assets such as characters, items, and land can be tokenized as ERC-721 tokens, providing players with true ownership of their in-game assets. Examples include Axie Infinity and Decentraland.
-
-- **Collectibles:** ERC-721 tokens can represent unique collectible items in a digital space. Examples include NBA Top Shot moments and CryptoPunks.
-
-- **Virtual Real Estate:** Virtual real estate can be tokenized as ERC-721 tokens, providing proof of ownership and facilitating trade on virtual platforms. Examples include parcels of land in Cryptovoxels and Decentraland.
-
----
-
-## Conclusion
-
-ERC-721, with its consistent framework for non-fungible tokens, has revolutionized the unique digital asset space on Ethereum. This standard, when contrasted with ERC-20, highlights Ethereum's capacity for both fungible and unique asset types. Adhering to the EIP-721 specification, ERC-721 tokens have significantly influenced the Ethereum-based digital economy. From digital art to gaming, these tokens underscore their importance and role as catalysts in the burgeoning NFT revolution.
-
----
-
-## See Also
-
-- [EIP-721: ERC-721 Token Standard](https://eips.ethereum.org/EIPS/eip-721)
-- [ERC-721 Token Standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-721/)
-
diff --git a/docs/learn/token-development/erc-721-token/implementing-an-erc-721-vid.mdx b/docs/learn/token-development/erc-721-token/implementing-an-erc-721-vid.mdx
deleted file mode 100644
index 9a5c511d7..000000000
--- a/docs/learn/token-development/erc-721-token/implementing-an-erc-721-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: Implementing an ERC-721
-description: Deploy your own NFT.
-hide_table_of_contents: false
-sidebarTitle: Implementation Guide
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
-
diff --git a/docs/learn/token-development/erc-721-token/openzeppelin-erc-721-vid.mdx b/docs/learn/token-development/erc-721-token/openzeppelin-erc-721-vid.mdx
deleted file mode 100644
index bd7d71bc0..000000000
--- a/docs/learn/token-development/erc-721-token/openzeppelin-erc-721-vid.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: OpenZeppelin ERC-721 Implementation
-description: Review the ERC-721 implementation by OpenZeppelin.
-hide_table_of_contents: false
-sidebarTitle: OpenZeppelin ERC-721
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
-
diff --git a/docs/learn/token-development/intro-to-tokens/intro-to-tokens-vid.mdx b/docs/learn/token-development/intro-to-tokens/intro-to-tokens-vid.mdx
deleted file mode 100644
index a70e30156..000000000
--- a/docs/learn/token-development/intro-to-tokens/intro-to-tokens-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Introduction
-sidebarTitle: Tokens Overview
-description: Welcome to the wonderful world of tokens!
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/intro-to-tokens/misconceptions-about-tokens-vid.mdx b/docs/learn/token-development/intro-to-tokens/misconceptions-about-tokens-vid.mdx
deleted file mode 100644
index 65b3a2dcf..000000000
--- a/docs/learn/token-development/intro-to-tokens/misconceptions-about-tokens-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Common Misconceptions
-description: Review some common misconceptions before starting.
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
-
diff --git a/docs/learn/token-development/intro-to-tokens/tokens-overview.mdx b/docs/learn/token-development/intro-to-tokens/tokens-overview.mdx
deleted file mode 100644
index f94ef6c97..000000000
--- a/docs/learn/token-development/intro-to-tokens/tokens-overview.mdx
+++ /dev/null
@@ -1,101 +0,0 @@
----
-title: Overview
-description: An overview of tokens on Ethereum
-hide_table_of_contents: false
-sidebarTitle: Overview Guide
----
-
-
-This article will provide an overview of the most popular token standards on Ethereum, including ERC-20, ERC-721, ERC-1155, and a discussion on their properties and various use cases.
-
----
-
-## Objectives:
-
-By the end of this lesson you should be able to:
-
-- Describe the properties of ERC-20 and ERC-721 tokens
-- List popular ERC-721 tokens
-- List the uses for ERC-20, ERC-721, and ERC-1155 tokens
-
----
-
-## ERC Token Standards
-
-Ethereum Request for Comments (or, ERC) is a term used to describe technical proposals and standards for Ethereum. An ERC is authored by developers and members of the Ethereum community to suggest improvements, new features, or guidelines for creating and managing tokens and smart contracts. Once an ERC is submitted, it undergoes review and discussion by the community. If it gains consensus, it can then be implemented or adopted as a standard in the ecosystem.
-
-Token standards on Ethereum form the backbone of the digital asset ecosystem. They are a set of predefined rules and guidelines that govern the creation, management, and interaction of tokens on the network. These standards ensure that tokens are compatible with various apps, wallets, and other tokens within the Ethereum ecosystem. Token standards allow developers to create tokens with consistent behavior, facilitating seamless interaction and interoperability within the network.
-
----
-
-## ERC-20
-
-ERC-20 tokens, the most widely-used token standard on Ethereum, possess several key properties that make them versatile and flexible for various applications. One of the defining characteristics of these tokens is their fungibility. Each unit of an ERC-20 token is interchangeable and holds equal value to another unit of the same token, rendering them indistinguishable from one another. In other words, one USDC token is always equal in value and interchangeable with another USDC token.
-
-Another aspect of ERC-20 tokens is their standardized interface, which includes a set of six mandatory functions: `totalSupply()`, `balanceOf(address)`, `transfer(address, uint256)`, `transferFrom(address, address, uint256)`, `approve(address, uint256)`, and `allowance(address, address)`. This standardization ensures consistency when interacting with these tokens, irrespective of their specific implementation or use case. For example, a user can easily check their token balance or transfer tokens using the same set of functions, whether they are interacting with a governance token like UNI or a stablecoin like DAI.
-
-Some notable applications of ERC-20 tokens include utility tokens (FIL, BAT, MANA), governance tokens (UNI, AAVE, COMP), and stablecoins (USDC, USDT, DAI).
-
-
-
-
-
----
-
-## ERC-721
-
-ERC-721 is a prominent token standard specifically designed for NFTs, allowing for the creation and management of unique, indivisible digital assets that each have their own special properties.
-
-In contrast to ERC-20 tokens, which are fungible and can be easily exchanged, ERC-721 tokens are non-fungible and can't be swapped on a one-to-one basis. Every token has its own attributes that set it apart from the rest. This one-of-a-kind nature enables the representation of a wide range of digital assets, including digital art, virtual real estate, and collectibles. For example, an artist could mint a one-of-a-kind digital painting, a virtual land parcel could be tokenized in a metaverse, or a rare sports card could be digitized as a collectible NFT.
-
-ERC-721 tokens, like ERC-20 tokens, follow a standardized interface but employ a unique set of functions designed for non-fungible tokens, which allow developers to interact with NFTs across multiple platforms. For instance, a developer would use the same set of functions to interact with a digital artwork NFT listed on OpenSea as they would with a virtual land parcel NFT in Decentraland.
-
-Besides their unique qualities, ERC-721 tokens come with metadata properties that offer information about the token's specific features, such as the artwork's title, the artist, and an image preview. This metadata helps users better understand and appreciate the distinct aspects of each NFT, and it is consistent across platforms.
-
-Some notable applications of ERC-721 tokens include digital art by Beeple, virtual collectibles by NBA Top Shot, virtual real estate in Decentraland, and Ethereum-based domain names like vitalik.eth on the Ethereum Name Service (ENS).
-
-
-
-
-
----
-
-## ERC-1155
-
-ERC-1155 is an innovative hybrid token standard that merges the best aspects of both fungible and non-fungible tokens, enabling developers to create and manage diverse token types using a single smart contract. This combination of features allows ERC-1155 tokens to provide greater versatility while representing a wide array of assets with different levels of fungibility.
-
-For example, a video game might use both fungible and non-fungible tokens within its ecosystem. Fungible tokens could represent in-game currencies, consumables, or resources, while non-fungible tokens could represent exclusive and unique items like character skins, weapons, or collectible cards.
-
-Digital artists can also benefit from ERC-1155, as it allows them to mint limited edition series of their artwork, with each piece in the series having unique attributes. At the same time, they can create fungible tokens that represent ownership of a specific edition number within the series.
-
-Similar to other token standards, ERC-1155 tokens adhere to a standardized interface with a set of functions that ensure consistency and compatibility across platforms and services. Furthermore, this standard enables efficient batch transfers, simplifying the process and reducing the cost of managing multiple tokens within a single application. For instance, a user who has collected various in-game items in a virtual world can leverage ERC-1155's batch transfer feature to send multiple fungible and non-fungible tokens to another user or marketplace simultaneously. This efficient approach minimizes transaction costs and the complexity typically involved in transferring numerous tokens one by one.
-
-
-
-
-
----
-
-## Other Token Standards
-
-In addition to the three most prominent token standards that we covered, it is worth mentioning that other standards like ERC-777 and ERC-4626 have been introduced to address specific use cases or challenges. ERC-777 offers enhanced security and functionality over the fungible ERC-20 standard, while ERC-4626 streamlines yield-bearing vault integration by optimizing and unifying technical parameters. These lesser-known standards highlight the ongoing innovation and adaptability of the Ethereum token ecosystem as it continues to grow and evolve.
-
----
-
-## Conclusion
-
-Ethereum's ERC token standards have played a pivotal role in shaping the digital asset ecosystem by providing clear guidelines and rules for the creation, management, and interaction of tokens on the network. From the widely-used ERC-20 standard for fungible tokens to the distinct ERC-721 standard for non-fungible tokens and the versatile hybrid ERC-1155 standard, these token standards empower developers to craft diverse tokens tailored to various use cases and applications. The standardized interfaces ensure seamless interoperability within the Ethereum ecosystem, facilitating token transfers and interactions across different platforms and services. Additional token standards, such as ERC-777 and ERC-4626, address specific challenges and further demonstrate the continuous innovation and adaptability of the Ethereum token ecosystem.
-
----
-
-## See Also
-
-- [EIP-20](https://eips.ethereum.org/EIPS/eip-20)
-- [EIP-721](https://eips.ethereum.org/EIPS/eip-721)
-- [EIP-1155](https://eips.ethereum.org/EIPS/eip-1155)
-- [EIP-777](https://eips.ethereum.org/EIPS/eip-777)
-- [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626)
-
-
-[token standards]: https://ethereum.org/en/developers/docs/standards/tokens/
-
diff --git a/docs/learn/token-development/minimal-tokens/creating-a-minimal-token-vid.mdx b/docs/learn/token-development/minimal-tokens/creating-a-minimal-token-vid.mdx
deleted file mode 100644
index 42a573685..000000000
--- a/docs/learn/token-development/minimal-tokens/creating-a-minimal-token-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Create a Minimal Token
-description: Learn to build a very simple token.
-sidebarTitle: Creatiing a Minimal Token
-hide_table_of_contents: false
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/minimal-tokens/minimal-token-sbs.mdx b/docs/learn/token-development/minimal-tokens/minimal-token-sbs.mdx
deleted file mode 100644
index afe5cf76d..000000000
--- a/docs/learn/token-development/minimal-tokens/minimal-token-sbs.mdx
+++ /dev/null
@@ -1,175 +0,0 @@
----
-title: Minimal Token
-description: Build your own minimal token.
-hide_table_of_contents: false
-sidebarTitle: Step by Step Guide
----
-
-At their core, tokens are very simple. The technology powering famous NFT collections and fungible tokens worth vast amounts of money simply uses the EVM to keep track of who owns what, and provides a permissionless way for the owner to transfer what they own to someone new.
-
----
-
-## Objectives
-
-By the end of this lesson you should be able to:
-
-- Construct a minimal token and deploy to testnet
-- Identify the properties that make a token a token
-
----
-
-## Implementing a Token
-
-The minimal elements needed for a token are pretty basic. Start by creating a contract called `MinimalToken`. Add a `mapping` to relate user addresses to the number of tokens they possess. Finally, add a variable to track `totalSupply`:
-
-
-
-```solidity
-contract MinimalToken {
- mapping (address => uint) public balances;
- uint public totalSupply;
-}
-```
-
-
-
-Add a `constructor` that initializes the `totalSupply` at 3000 and assigns ownership to the contract creator:
-
-
-
-```solidity
-constructor() {
- totalSupply = 3000;
-
- balances[msg.sender] = totalSupply;
-}
-```
-
-
-
-Deploy and test to confirm that the total supply is 3000, and the balance of the first account is as well.
-
-
-
-
-
-Update the constructor and hardcode a distribution of the tokens to be evenly split between the first three test accounts:
-
-
-
-```solidity
-constructor() {
- totalSupply = 3000;
-
- balances[msg.sender] = totalSupply / 3;
- balances[0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2] = totalSupply / 3;
- balances[0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db] = totalSupply / 3;
-}
-```
-
-
-
-Redeploy and test again. Now, each of the first three accounts should have 1000 tokens.
-
-
-
-
-
----
-
-## Transferring Tokens
-
-We can set an initial distribution of tokens and we can see balances, but we're still missing a way to allow the owners of these tokens to share them or spend them.
-
-To remediate this, all we need to do is add a function that can update the balances of each party in the transfer.
-
-Add a `function` called `transfer` that accepts an `address` of `_to` and a `uint` for the `_amount`. You don't need to add anything for `_from`, because that should only be `msg.sender`. The function should subtract the `_amount` from the `msg.sender` and add it to `_to`:
-
-
-
-```solidity
-function transfer(address _to, uint _amount) public {
- balances[msg.sender] -= _amount;
- balances[_to] += _amount;
-}
-```
-
-
-
-Double-check that you've switched back to the first address and redeploy. Then, try sending 500 tokens to the second address.
-
-
-
-
-
-What happens if you try to transfer more tokens than an account has? Give it a try!
-
-```text
-transact to MinimalToken.transfer pending ...
-transact to MinimalToken.transfer errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-Note: The called function should be payable if you send value and the value you send should be less than your current balance.
-Debug the transaction to get more information.
-```
-
-You won't be able to do it, though the `Note:` here is **misleading**. In the EVM, `payable` **only** refers to transfers of the primary token used to pay gas fees: ETH, Base ETH, Sepolia ETH, Matic, etc. It does **not** refer to the balance of our simple token.
-
-Instead, the transaction is reverting because of the built-in overflow/underflow protection. It's not a great programming practice to depend on this, so add an error for `InsufficientTokens` that returns the `newSenderBalance`.
-
-```Solidity
-function transfer(address _to, uint _amount) public {
- int newSenderBalance = int(balances[msg.sender] - _amount);
- if (newSenderBalance < 0) {
- revert InsufficientTokens(newSenderBalance);
- }
-
- balances[msg.sender] = uint(newSenderBalance);
- balances[_to] += _amount;
-}
-```
-
-Try spending too much again. You'll get the same error in Remix:
-
-```text
-transact to MinimalToken.transfer pending ...
-transact to MinimalToken.transfer errored: VM error: revert.
-
-revert
- The transaction has been reverted to the initial state.
-Note: The called function should be payable if you send value and the value you send should be less than your current balance.
-Debug the transaction to get more information.
-```
-
-However, you can use the debug tool to review the error in memory to see that it now matches your custom `error`.
-
-## Destroying Tokens
-
-Tokens can be effectively destroyed by accident, or on purpose. Accidental destruction happens when someone sends a token to an unowned wallet address. While it's possible that some day, some lucky person will create a new wallet and find a pleasant surprise, the most likely outcome is that any given randomly chosen address will never be used, thus no one will ever have the ability to use or transfer those tokens.
-
-Luckily, there are some protections here. Similar to credit card numbers, addresses have a built-in checksum that helps protect against typos. Try it out by trying to transfer tokens to the second Remix address, but change the first character in the address from `A` to `B`. You'll get an error:
-
-```text
-transact to MinimalToken.transfer errored: Error encoding arguments: Error: bad address checksum (argument="address", value="0xBb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", code=INVALID_ARGUMENT, version=address/5.5.0) (argument=null, value="0xBb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", code=INVALID_ARGUMENT, version=abi/5.5.0)
-```
-
-A more guaranteed way to destroy, or _burn_ a token, is to transfer it to the default address `0x0000000000000000000000000000000000000000`. This address is unowned and unownable, making it mathematically impossible to retrieve any tokens that are sent to it. Redeploy and try it out by sending 1000 tokens to the zero address.
-
-The `totalSupply` remains unchanged, and the balance of the zero address is visible, but those tokens are stuck there forever.
-
-
-The [zero address] currently has a balance of more than 11,000 ETH, worth over **20 million dollars**! Its total holding of burned assets is estimated to be worth more than **200 million dollars**!!!
-
-
-
----
-
-## Conclusion
-
-In this lesson, you've learned to implement a simple token, which is really just a system to store the balance of each address, and a mechanism to transfer them from one wallet to another. You've also learned how to permanently destroy tokens, whether by accident, or on purpose.
-
----
-
-[zero address]: https://etherscan.io/address/0x0000000000000000000000000000000000000000
-
diff --git a/docs/learn/token-development/minimal-tokens/minimal-tokens-exercise.mdx b/docs/learn/token-development/minimal-tokens/minimal-tokens-exercise.mdx
deleted file mode 100644
index 151b0e83b..000000000
--- a/docs/learn/token-development/minimal-tokens/minimal-tokens-exercise.mdx
+++ /dev/null
@@ -1,72 +0,0 @@
----
-title: Minimal Tokens Exercise
-description: Exercise - Create your own token!
-hide_table_of_contents: false
-sidebarTitle: Exercise
----
-
-Create a contract that adheres to the following specifications.
-
----
-
-## Contract
-
-Create a contract called `UnburnableToken`. Add the following in storage:
-
-- A public mapping called `balances` to store how many tokens are owned by each address
-- A `public uint` to hold `totalSupply`
-- A `public uint` to hold `totalClaimed`
-- Other variables as necessary to complete the task
-
-Add the following functions.
-
-### Constructor
-
-Add a constructor that sets the total supply of tokens to 100,000,000.
-
-### Claim
-
-Add a `public` function called `claim`. When called, so long as a number of tokens equalling the `totalSupply` have not yet been distributed, any wallet _that has not made a claim previously_ should be able to claim 1000 tokens. If a wallet tries to claim a second time, it should revert with `TokensClaimed`.
-
-The `totalClaimed` should be incremented by the claim amount.
-
-Once all tokens have been claimed, this function should revert with the error `AllTokensClaimed`. (We won't be able to test this, but you'll know if it's there!)
-
-### Safe Transfer
-
-Implement a `public` function called `safeTransfer` that accepts an address `_to` and an `_amount`. It should transfer tokens from the sender to the `_to` address, **only if**:
-
-- That address is not the zero address
-- That address has a balance of greater than zero Base Sepolia Eth
-
-A failure of either of these checks should result in a revert with an `UnsafeTransfer` error, containing the address.
-
----
-
-### Submit your Contract and Earn an NFT Badge! (BETA)
-
-
-#### Hey, where'd my NFT go!?
-
-[Testnets](/learn/deployment-to-testnet/test-networks) are not permanent! Base Goerli [will soon be sunset](https://base.mirror.xyz/kkz1-KFdUwl0n23PdyBRtnFewvO48_m-fZNzPMJehM4), in favor of Base Sepolia.
-
-As these are separate networks with separate data, your NFTs **will not** transfer over.
-
-**Don't worry!** We've captured the addresses of all NFT owners on Base Goerli and will include them when we release the mechanism to transfer these NFTs to mainnet later this year! You can also redeploy on Sepolia and resubmit if you'd like!
-
-
-
-
-The contract specification contains actions that can only be performed once by a given address. As a result, the unit tests for a passing contract will only be successful the **first** time you test.
-
-**You may need to submit a fresh deployment to pass**
-
-
-
-{/* */}
-
-
diff --git a/docs/learn/token-development/minimal-tokens/transferring-a-minimal-token-vid.mdx b/docs/learn/token-development/minimal-tokens/transferring-a-minimal-token-vid.mdx
deleted file mode 100644
index c9439826a..000000000
--- a/docs/learn/token-development/minimal-tokens/transferring-a-minimal-token-vid.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Transferring a Minimal Token
-description: Explore how tokens are given from one owner to another.
-hide_table_of_contents: false
-sidebarTitle: Transferring Tokens
----
-
-import { Video } from '/snippets/VideoPlayer.mdx';
-
-
diff --git a/docs/learn/token-development/nft-guides/complex-onchain-nfts.mdx b/docs/learn/token-development/nft-guides/complex-onchain-nfts.mdx
deleted file mode 100644
index 6b3d9bbf9..000000000
--- a/docs/learn/token-development/nft-guides/complex-onchain-nfts.mdx
+++ /dev/null
@@ -1,867 +0,0 @@
----
-title: 'Complex Onchain NFTs'
-slug: /complex-onchain-nfts
-description: A tutorial that teaches how to make complex nfts that are procedurally generated and have onchain metadata and images.
-author: briandoyle81
-
----
-
-# Complex Onchain NFTs
-
-Many NFTs are dependent on offchain metadata and images. Some use immutable storage locations, such as [IPFS]. Others use traditional web locations, and many of these allow the owner of the contract to modify the URL returned by a contract when a site or user attempts to retrieve the location of the token art and metadata. This power isn't inherently bad, because we probably want someone to be able to fix the contract if the storage location goes down. However, it does introduce a requirement to trust the contract owner.
-
-Although challenging, it is possible to write a smart contract that contains all the necessary logic and data to generate json metadata and SVG images, entirely onchain. It **will** be expensive to deploy, but will be as cheap as simpler contracts to mint!
-
-In this tutorial, we'll show you how to do this to create your own fully-onchain art project, similar to our [sample project].
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Programmatically generate and return json metadata for ERC-721 tokens
-- Deterministically construct unique SVG art in a smart contract
-- Generate deterministic, pseudorandom numbers
-
-## Prerequisites
-
-### ERC-721 Tokens
-
-This tutorial assumes that you are able to write, test, and deploy your own ERC-721 tokens using the Solidity programming language. If you need to learn that first, check out our content in [Base Learn] or the sections specific to [ERC-721 Tokens]!
-
-### Vector Art
-
-You'll need some familiarity with the SVG art format and a basic level of ability to edit and manipulate vector art. If you don't have this, find an artist friend and collaborate!
-
-## Creating the Art Assets
-
-To start, you'll need to build a few vector art assets and mock up an example of what your NFT might look like. Later, you'll cut these up and format them in a way that your smart contract will use to assemble unique NFTs for each minter.
-
-The mockup needs to have all of the elements you plan to have in the NFT, and you should be able to manually move things around or change colors to make it so that you can create the range of variation you want. For example:
-
-- A gradient sky in which the colors are randomized
-- One of three styles of sun, always in the same spot in the upper right corner
-- One to five clouds placed randomly in the upper half of the canvas
-- A wide mountain ridge that will always be in the middle, but slide side to side to show a different part for each NFT
-- An ocean in the foreground that is always the same
-
-If you are an artist, or are working with one, you can use the vector drawing tool of your choice to assemble your mockup. If not, you can use a number of stock art or AI tool options to assist you. If you do, make sure you understand any relevant laws or terms of service!
-
-You can also work from ours: [Sample Art]
-
-Either way, you should end up with something similar to this:
-
-
-
-
-
-## The Art of Making it Fit
-
-You'll notice that the SVG is probably way too big to be placed in a smart contract. The example is 103 KB, so you'll have to be clever to make this work.
-
-You'll accomplish this task by splitting each element out of the mockup and deploying them into separate smart contracts. To do so, individually export each element, and make sure that the exported pieces are no bigger than about 15 KB. That way, you'll have enough space to fit each piece within the 24KiB limit for compiled bytecode.
-
-If you're working with the sample, you'll end up with individual SVGs for:
-
-- Sun 1: 9 KB
-- Sun 2: 9 KB
-- Sun 3: 9 KB
-- Ocean: 17 KB
-- Mountain: 14 KB
-- Cloud: 6 KB
-- Sky: 802 bytes
-
-If you don't have the tools to do this, you can find these files here: [Sample Art]
-
-## Contract Architecture
-
-You'll need to build and deploy a number of contracts for this project. They'll be organized in this architecture:
-
-
-
-
-
-Deploying this many contracts will have a cost associated with it, but once they're deployed, this contract will cost the same as any other NFT contract. Remember, `pure` and `view` functions called outside the blockchain don't cost any gas. This means that you can use multiple contracts to assemble a relatively large graphic without additional costs!
-
-## Building the Contracts
-
-Create a new project using the toolkit of your choice, and add a contract called `LandSeaSkyNFT`. Import OpenZeppelin's ERC-721, inherit from it, and set it up with the constructor, a mint function, and a counter to keep track of the token ID:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
-
-contract LandSeaSkyNFT is ERC721 {
- uint public counter;
-
- constructor() ERC721("Land, Sea, and Sky", "LSS") {}
-
- function mint() public {
- counter++;
- _safeMint(msg.sender, counter);
- }
-}
-```
-
-### Overriding the `_baseURI()` Function
-
-Normally, you'd override `_baseURI()` with the base URL for the location you select to keep your NFT metadata. This could be a website, IPFS folder, or many other possible locations.
-
-Since this contract will be generating the .json file directly, instead set it to indicate this to the browser:
-
-```solidity
-function _baseURI() internal pure override returns (string memory) {
- return "data:application/json;base64,";
-}
-```
-
-### Importing the Base64 Library
-
-As indicated above, you'll be returning the json metadata in [Base64] format. OpenZeppelin has a utility contract to do this. You'll also need the `Strings` library. Go ahead and import them:
-
-```solidity
-import "@openzeppelin/contracts/utils/Base64.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-```
-
-
-Base64 allows the transport of binary data over the web in a reliable way. It is not a compression algorithm, and actually increased the data size by a 4/3 ratio.
-
-
-### Planning the override for the `tokenURI()` Function
-
-Next, set up your `tokenURI` function override. You'll need to write some other contracts to make this work, but you can write most of the code and stub out a plan for the rest to:
-
-- Check and ensure the token ID exists
-- Compile the json metadata for the token, including:
- - The `"name"` of the NFT
- - A `"description"`
- - The `"image"`
-- Base64 encode the above, combine it with the `_baseURI` and return it.
-
-```solidity
-function tokenURI(uint _tokenId) public view override returns (string memory) {
- if(_tokenId > counter) {
- revert InvalidTokenId(_tokenId); // Don't forget to add the error above!
- }
-
- string memory json = Base64.encode(
- bytes(
- string(
- abi.encodePacked(
- '{"name": "Land, Sea, and Sky #: ',
- Strings.toString(_tokenId),
- '", "description": "Land, Sea, and Sky is a collection of generative art pieces stored entirely onchain.", "image": "data:image/SVG+xml;base64,',
- "TODO: Build the SVG with the token ID as the seed",
- '"}'
- )
- )
- )
- );
-
- return string(abi.encodePacked(_baseURI(), json));
-}
-```
-
-
-Getting the quotes and commas correct when the json is broken apart like this is challenging. When debugging, look here first!
-
-
-### Test Your Progress
-
-Test your function by writing a simple test to mint an NFT, then call and log the output of the `tokenURI` function. You should get something similar to:
-
-```text
-string: data:application/json;base64,eyJuYW1lIjogIkxhbmQsIFNlYSwgYW5kIFNreSAjMSIsICJkZXNjcmlwdGlvbiI6ICJMYW5kLCBTZWEsIGFuZCBTa3kgaXMgYSBjb2xsZWN0aW9uIG9mIGdlbmVyYXRpdmUgYXJ0IHBpZWNlcyBzdG9yZWQgZW50aXJlbHkgb25jaGFpbi4iLCAiaW1hZ2UiOiAiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxUT0RPOiBCdWlsZCB0aGUgU1ZHIHdpdGggdGhlIHRva2VuIElEIGFzIHRoZSBzZWVkIn0=
-```
-
-To see if it worked, you'll need to use a manual method to decode the base64 data; everything after the comma:
-
-```text
-eyJuYW1lIjogIkxhbmQsIFNlYSwgYW5kIFNreSAjMSIsICJkZXNjcmlwdGlvbiI6ICJMYW5kLCBTZWEsIGFuZCBTa3kgaXMgYSBjb2xsZWN0aW9uIG9mIGdlbmVyYXRpdmUgYXJ0IHBpZWNlcyBzdG9yZWQgZW50aXJlbHkgb25jaGFpbi4iLCAiaW1hZ2UiOiAiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxUT0RPOiBCdWlsZCB0aGUgU1ZHIHdpdGggdGhlIHRva2VuIElEIGFzIHRoZSBzZWVkIn0=
-```
-
-You can use the terminal: `echo -n '' | base64 --decode`
-
-Do so, and you'll get:
-
-```text
-{"name": "Land, Sea, and Sky #: 1", "description": "Land, Sea, and Sky is a collection of generative art pieces stored entirely onchain.", "image": "data:image/SVG+xml;base64,TODO: Build the SVG with the token ID as the seed"}
-```
-
-## Building the SVG
-
-Next, you need to build logic to compile a real, working SVG from the pieces you've saved. You'll also need to add some variation based on the ID of the NFT.
-
-### SVG Renderer Contract
-
-Add a new file and contract called `SVGRenderer`. It doesn't need a constructor, but it will need the `Strings` library:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-contract SVGRenderer {
-
-}
-```
-
-Open the exemplar SVG in a code editor, and using it as an example, build out a function that uses `abi.encodePacked` to build everything from the SVG **except** the actual art. That's much too big for one contract, so add stubs instead.
-
-Depending on the tool you used to make the SVG, there may be unneeded extras you can remove from these lines. You also **don't** need the items in `` or ``. You'll take advantage of the flexibility of the format to include those in the pieces returned by the supporting contract.
-
-```solidity
-function render(uint _tokenId) public view returns (string memory) {
- return string(
- abi.encodePacked(
- ""
- )
- );
-}
-```
-
-### Rendering the Sea
-
-The sea element of this NFT will be the same for all NFTs, so it makes sense to write that contract first. Create it called, `SeaRenderer`, with a function called `render`. The `` element is the root of the different pieces of the SVG, so add that and a stub for the rest.
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-contract SeaRenderer {
- function render() public pure returns (string memory) {
- return // TODO: Render the sea
- }
-}
-```
-
-The next part is tricky, and a little messy. You'll need to combine parts of the individually exported SVG that has the sea art and all of its properties with the position data for this part of the art from the exemplar SVG. You'll then need to flatten it to a single line, and add it as a string constant.
-
-Start by opening the Ocean SVG. Change the viewBox to `viewBox="0 0 1024 1024"`. Move the `` and `` tag inside of the `` tag. Open the SVG in the browser to make sure it hasn't broken.
-
-Next, delete the `id` and `data-name` from the top level `` and experiment with the `transform="translate(20,2.5)"` property to move the art back down to the bottom of the viewport.
-
-With the sample art, `` should work.
-
-The last edits you need to make are **critical** - do a find/replace to change all of the `cls-1` and similar classnames, to `cls-land-1`! Otherwise, the classes will override one another and nothing will be the right color. Also find all instances of `linear-gradient` and do the same.
-
-**Make sure** you change both the definitions, and where they're called!
-
-Finally, use the tool of your choice to minify **only** the outermost `` tag and its contents. This will flatten the code to a single line and remove extra empty character spaces. Doing so makes it easier to add to your contract, and makes the data smaller. Add it as a constant string to `SeaRenderer.sol`:
-
-```solidity
-string constant SVG = '';
-```
-
-You may need to do a find/replace and ensure you're using only one type of quote in the SVG.
-
-Replace your `TODO` with the constant.
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-string constant SVG = ;
-
-contract SeaRenderer {
- function render() public pure returns (string memory) {
- return SVG;
- }
-}
-```
-
-Test this function independently and make sure that if you paste the content inside a set of `
-
-```
-
-You have some design choices to make here. You could use deterministically chosen, but essentially random colors to make up the gradient. However, doing so will lead to the vast majority of NFTs having truly bizarre colors that don't look nice and don't look like they belong with the rest of the art.
-
-No one wants their sky to be a mix of cyan, teal, maroon, and brown!
-
-It might be better to add a gentle modification to the existing gradient colors and range. Start by minifying the top level `` group and contents, then create **two** constant strings, one for everything before the first `` element, and one for everything after the last `` element.
-
-Neither string should have the ``s. You'll make those next.
-
-### Building the Renderer Contract
-
-Add a new file and contract called `SkyRenderer`. Add your strings:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-string constant START = '';
-string constant END = '';
-
-contract SkyRenderer {
- function render(uint _tokenId) public pure returns (string memory) {
- return //TODO;
- }
-}
-```
-
-Next, add constants for the existing `offset` and `stop-color` properties:
-
-```solidity
-string constant OFFSET1 = ".12";
-string constant OFFSET2 = ".34";
-string constant OFFSET3 = ".68";
-string constant OFFSET4 = ".96";
-string constant COLOR1 = "#c391b4";
-string constant COLOR2 = "#ce9f9b";
-string constant COLOR3 = "#dfd061";
-string constant COLOR4 = "#e3f9f7";
-```
-
-Now, stub out your return for the `render` function. It will use the built in method of using `abi.encode` and casting to `string` to combine all the parts and return them.
-
-```solidity
-function render(uint _tokenId) public pure returns (string memory) {
- return string(
- abi.encodePacked(
- START,
- // TODO stop 1,
- // TODO stop 2,
- // TODO stop 3,
- // TODO stop 4,
- END
- )
- );
-}
-```
-
-Do the same for a function to `buildStop`:
-
-```solidity
-function _buildStop(
- string memory _offset,
- string memory _color,
- uint _tokenId,
- uint _stopNumber
- ) internal pure returns (string memory) {
- return string(
- abi.encodePacked(
- ''
- )
- );
- }
-```
-
-Now, you just need to figure out how to modify the properties based on the `_tokenId`. It needs to be "random" in the sense that every NFT should be different, but it has to be deterministic, so that you get the same art every time you load the image.
-
-First, **subtract 10** from the values and convert them to `uints` **without** decimals in each of your stop constants, and reduce the last to `80`:
-
-```solidity
-uint constant OFFSET1 = 2;
-uint constant OFFSET2 = 24;
-uint constant OFFSET3 = 58;
-uint constant OFFSET4 = 80;
-```
-
-You'll also need to update the parameter in `_buildStop`.
-
-Add a function to `_buildOffsetValue`. This will pick an integer between 0 and 20 for each offset, and add it to the modified offsets you just made. The result will be a change of + or 1 10 for each value (with the last being slightly different to keep it in range):
-
-```solidity
-function _buildOffsetValue(
- uint _offset,
- uint _tokenId,
- uint _stopNumber
- ) internal pure returns (string memory) {
- bytes32 hash = keccak256(abi.encodePacked(_offset, _tokenId, _stopNumber));
- uint rand = uint(hash);
- uint change = rand % 20; // Produces a number between 0 and 19
- if(change >= 10) {
- return string(
- abi.encodePacked(
- '.',
- Strings.toString(_offset + change)
- )
- );
- } else {
- return string(
- abi.encodePacked(
- '.',
- '0', // 9 is .09, not .9
- Strings.toString(_offset + change)
- )
- );
- }
-}
-```
-
-This function uses hashing to create a pseudo-random number with the token id and stop as seeds, guaranteeing a consistent value, unique for each token and each stop within that token. It takes advantage of the way the offset property is interpreted - in this case, `".12+.20" == ".32"`.
-
-Finally, update your `render` function to call `_buildStop`:
-
-```solidity
-function render(uint _tokenId) public pure returns (string memory) {
- return string(
- abi.encodePacked(
- START,
- _buildStop(OFFSET1, COLOR1, _tokenId, 1),
- _buildStop(OFFSET2, COLOR2, _tokenId, 2),
- _buildStop(OFFSET3, COLOR3, _tokenId, 3),
- _buildStop(OFFSET4, COLOR4, _tokenId, 4),
- END
- )
- );
-}
-```
-
-### Incorporating the Sky Renderer
-
-Return to `SVGRenderer.sol` and add an instance of `ISVGPartRenderer` for the skyRenderer. Add an argument to the `constructor` and initialize it, then call the `render` function in place of your `TODO` for the background.
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-interface ISVGPartRenderer {
- function render() external pure returns (string memory);
- function render(uint _tokenId) external pure returns (string memory);
-}
-
-contract SVGRenderer {
-
- ISVGPartRenderer seaRenderer;
- ISVGPartRenderer skyRenderer;
-
- constructor(address _seaRenderer, address _skyRenderer) {
- seaRenderer = ISVGPartRenderer(_seaRenderer);
- skyRenderer = ISVGPartRenderer(_skyRenderer);
- }
-
- function render(uint _tokenId) public view returns (string memory) {
- return string(
- abi.encodePacked(
- "",
- // TODO: Add the clouds,
- // TODO: Add the sun,
- // TODO: Add the land,
- skyRenderer.render(_tokenId),
- seaRenderer.render(),
- ""
- )
- );
- }
-}
-```
-
-Update your deploy script, then deploy and test as before.
-
-```tsx
-const SkyRenderer = await deploy('SkyRenderer', {
- from: deployer,
-});
-
-const SVGRenderer = await deploy('SVGRenderer', {
- from: deployer,
- args: [SeaRenderer.address, SkyRenderer.address],
-});
-```
-
-Test as before. Your NFTs now have the sky!
-
-## Adding the LandRenderer
-
-Next up is the mountain part of the SVG. For this, you'll change the horizontal translation left to right to show a different part of the mountains for each NFT.
-
-### Preparing the SVG
-
-Open the mountain SVG in both your browser and the editor. Once again, set the `viewBox` to 1024x1024 and move the `` and `` inside the top-level group (``).
-
-Find transform/translate values that first put the mountains so that they are at the bottom, and the left-most portion is shown, then the right-most. `transform="translate(-150,350)"` and `transform="translate(-800,350)"` are about right.
-
-Don't forget to add `-land` to the classnames!
-
-### Writing the Contract
-
-Add a file and stub for the `LandRenderer`:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-contract LandRenderer {
- function render(uint _tokenId) public pure returns (string memory) {
- return string(
- abi.encodePacked(
- '',
- END
- )
- );
- }
-}
-```
-
-Minify the top-level `` element, and add a constant with everything after the opening `` tag. Use similar techniques as before to generate an offset based on the token id, then build the SVG. You'll end up with something like this:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-string constant END = "";
-
-contract LandRenderer {
- function render(uint _tokenId) public pure returns (string memory) {
- return string(
- abi.encodePacked(
- '',
- END
- )
- );
- }
-
- function _buildOffset(uint _tokenId) internal pure returns (string memory) {
- bytes32 hash = keccak256(abi.encodePacked(_tokenId));
- uint rand = uint(hash);
- uint xOffset = (rand % 650) + 150; // Produces a number between 150 and 799
- return string(abi.encodePacked("-", Strings.toString(xOffset)));
- }
-}
-```
-
-### Incorporating the LandRenderer
-
-Update `SVGRenderer`:
-
-```solidity
-ISVGPartRenderer seaRenderer;
-ISVGPartRenderer skyRenderer;
-ISVGPartRenderer landRenderer;
-
-constructor(address _seaRenderer, address _skyRenderer, address _landRenderer) {
- seaRenderer = ISVGPartRenderer(_seaRenderer);
- skyRenderer = ISVGPartRenderer(_skyRenderer);
- landRenderer = ISVGPartRenderer(_landRenderer);
-}
-
-function render(uint _tokenId) public view returns (string memory) {
- return string(
- abi.encodePacked(
- "",
- skyRenderer.render(_tokenId),
- landRenderer.render(_tokenId),
- seaRenderer.render(),
- ""
- )
- );
-}
-```
-
-And the deploy script:
-
-```tsx
-const LandRenderer = await deploy('LandRenderer', {
- from: deployer,
-});
-
-const SVGRenderer = await deploy('SVGRenderer', {
- from: deployer,
- args: [SeaRenderer.address, SkyRenderer.address, LandRenderer.address],
-});
-```
-
-Test as before. It's starting to look really nice!
-
-
-
-
-
-## Adding the Sun Renderer
-
-The sun renderer will use similar techniques as those you've already incorporated. The sun will be in the same place for all NFTs. Variation will come from each one having only one of the three suns shown in the exemplar art file.
-
-### Preparing the SVGs
-
-For each of the three sun SVGs:
-
-- Change the `viewBox` to 1024x1024
-- Move the `` and `` into the first group
-- Find the correct translation to put the sun in the upper right
- - 750, 100 should work with the sample art
-- Add `-sun` to the classnames
-
-### Writing the Contracts
-
-The tricky part here is that you can't fit all the suns into one contract. They're too big! Instead, split them into three, similar to the original ocean renderer. For example:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.20;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-
-string constant SVG = ''
-
-contract SunRenderer1 {
- function render() public pure returns (string memory) {
- return SVG;
- }
-}
-```
-
-### Incorporating the SunRenderer
-
-Add the three `SunRenderer`s as you have the other rendering contracts. You'll have to incorporate this one a little differently. Add a function that picks which `SunRenderer` to call, based on the NFT id.
-
-```solidity
-function pickSunRenderer(uint _tokenId) public view returns (ISVGPartRenderer) {
- bytes32 hash = keccak256(abi.encodePacked(_tokenId));
- uint rand = uint(hash);
- uint sun = rand % 3;
- if(sun == 0) {
- return sunRenderer1;
- } else if(sun == 1) {
- return sunRenderer2;
- } else {
- return sunRenderer3;
- }
- }
-```
-
-Make sure to put it after `skyRenderer` in the main `render` function!
-
-```solidity
-function render(uint _tokenId) public view returns (string memory) {
- return string(
- abi.encodePacked(
- "",
- skyRenderer.render(_tokenId),
- pickSunRenderer(_tokenId).render(),
- landRenderer.render(_tokenId),
- seaRenderer.render(),
- ""
- )
- );
-}
-```
-
-Test it out. You've now got one of three suns in the sky for each NFT!
-
-## Adding the Cloud Renderer
-
-On your own, try adding the clouds. The clouds renderer should:
-
-- Randomly select between one and seven clouds
-- Place those clouds randomly on the top half of the canvas
-- Be on the layer on top of the sun and sky, but below the sea and mountains
-
-## Conclusion
-
-In this tutorial, you've learned how to take advantage of the fact that offchain calls to your smart contracts don't use gas to create a significantly complex system to render complicated and unique NFTs, with the metadata and art existing entirely onchain. You've also explored some techniques for creating deterministic, pseudorandom numbers, and how to use them to add variation to your art. You've dealt with some of the many caveats and quirks of programmatically combining SVG files. Finally, you've practiced building multiple contracts that interact with one another.
-
-[Base Learn]: https://base.org/learn
-[ERC-721 Tokens]: https://docs.base.org/learn/erc-721-token/erc-721-standard-video
-[IPFS]: https://ipfs.tech/
-[Base64]: https://en.wikipedia.org/wiki/Base64
-[Hardhat and Hardhat Deploy]: https://docs.base.org/learn/hardhat-deploy/hardhat-deploy-sbs
-[testnet version of Opensea]: https://testnets.opensea.io/
-[sample project]: https://github.com/base-org/land-sea-and-sky
-[Sample Art]: https://github.com/base-org/land-sea-and-sky/tree/master/Final_SVGs
-[Basescan]: https://sepolia.basescan.org/
-
diff --git a/docs/learn/token-development/nft-guides/dynamic-nfts.mdx b/docs/learn/token-development/nft-guides/dynamic-nfts.mdx
deleted file mode 100644
index fc1212449..000000000
--- a/docs/learn/token-development/nft-guides/dynamic-nfts.mdx
+++ /dev/null
@@ -1,313 +0,0 @@
----
-title: 'Building dynamic NFTs'
-description: A tutorial that teaches how to make dynamic NFTs that evolve based on onchain or offchain actions.
-authors:
- - lukecd
-tags: ['nft']
-difficulty: intermediate
-hide_table_of_contents: false
-
----
-
-# Build a Dynamic NFT on Base with Irys
-
-In this tutorial, you will create a dynamic NFT using Irys's [mutability features].
-
-
-
-
-
-Dynamic NFTs are NFTs whose metadata evolves over time. They are commonly used in:
-
-- Gaming projects where in-game assets evolve as players progress
-- Loyalty programs where NFTs evolve as users accumulate points
-
-## Objectives
-
-By the end of this tutorial, you should be able to:
-
-- Permanently upload data onchain using Irys
-- Create NFT metadata and use it to mint an NFT
-- "Mutate" (change) the NFT metadata using Irys's mutability features
-
-## Prerequisites
-
-1. **Setup a Coinbase Wallet:** You'll need a web3 wallet; you'll use it to deploy the NFT contract and to sign and pay for uploads to Irys. We recommend using the [Coinbase Wallet].
-
-2. **Wallet Funding:** You'll need to fund your wallet with some Base Sepolia tokens. You can do this for free using the [Base Faucet].
-
-## About Irys
-
-[Irys] is a datachain, a blockchain optimized for data storage. Data uploaded to Irys is:
-
-- Permanent and immutable
-- Onchain
-- Censorship-resistant
-- Blockchain agnostic
-- Unconstrained (you can upload files of any size)
-
-When you store your NFT assets on Irys and mint them using a smart contract on Base, you can guarantee the NFT will be retrievable forever. Creators can rest assured that their works will endure indefinitely, while collectors can feel secure knowing their NFTs are permanently preserved.
-
-### Irys + Base
-
-Irys has a pay-once-store-forever model and accepts payment for storage using multiple tokens, including ETH on Base.
-
-## "Mutability"
-
-Data on Irys is permanent and immutable, but you use Irys's [mutability features] to simulate mutability and create dynamic NFTs that evolve based on onchain or offchain actions.
-
-
-
-
-
-Using Irys's mutability features, you create a single, static URL that is linked to a series of transactions. Then, you can add a new transaction to the series at any time, and the URL will always resolve to the most recent transaction.
-
-You'll mint your NFT using a mutable-style URL, and then push updates to that URL. The URL won't change, but the metadata it resolves to will.
-
-## About
-
-This tutorial focuses on creating a SuperMon NFT, similar to one used in a web3 game that would evolve during gameplay. The NFT starts with a basic appearance and can be "upgraded" twice. You will use the Irys CLI to "mutate" the metadata, simulating the automatic changes that would occur through player interactions in an actual game.
-
-## Smart contract
-
-You're building an NFT, which means you need a smart contract. Here's a simple one you can use to mint the NFT you'll create.
-
-```solidity filename="SuperMon.sol"
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
-
-// Import OpenZeppelin's ERC721 and ERC721URIStorage contracts
-// These URLs are compatible with Remix IDE
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
-import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
-import "@openzeppelin/contracts/access/Ownable.sol";
-
-contract SuperMon is ERC721URIStorage {
- uint256 private _tokenIdCounter;
-
- // No arguments in the constructor, the owner will be the contract deployer
- constructor() ERC721("SuperMon", "SMON") {
- _tokenIdCounter = 0;
- }
-
- // Mint function to create a new NFT
- function mint(address to, string memory uri) public {
- uint256 tokenId = _tokenIdCounter;
- _tokenIdCounter += 1;
- _safeMint(to, tokenId);
- _setTokenURI(tokenId, uri);
- }
-}
-
-```
-
-Deploy the smart contract using [Remix].
-
-## Irys CLI
-
-You'll use the [Irys CLI] to upload images and metadata.
-
-### Installing the CLI
-
-Install the Irys CLI globally using the `-g` flag. Depending on your setup, you may need to use sudo.
-
-```bash
-npm i -g @irys/cli
-```
-
-Or:
-
-```bash
-sudo npm i -g @irys/cli
-```
-
-### Using private keys
-
-When executing CLI commands involving funding and signing, you must provide a private key. Use the `-w` flag to specify a private key along with the `-t` flag to signal you're using ETH on Base.
-
-```console
-irys -w -t base-eth
-```
-
-## Uploading the images
-
-
-
-
-
-[Download a zip containing PNGs] for each level, and save them on your local drive.
-
-Next, fund Irys with 0.1 [Base Sepolia ETH] to pay for your uploads.
-
-> In all of these CLI examples, make sure to replace the value of the `-w` parameter with your own private key.
-
-```console
-irys fund 100000000000000000 \
- -n devnet \
- -t base-eth \
- -w 6dd5e....54a120201cb6a \
- --provider-url https://sepolia.base.org
-```
-
-> The `fund` command accepts a value in atomic units, 0.1 ETH is equal to 100000000000000000 in atomic units.
-
-Next, use the Irys CLI to upload each of the images to the Irys Devnet.
-
-> Uploads to Irys's devnet are kept for ~60 days and are paid for using free tokens available from [faucets].
-
-```console
-irys upload image-level-1.png \
- -n devnet \
- -t base-eth \
- -w 6dd5e....54a120201cb6a \
- --provider-url https://sepolia.base.org
-
-irys upload image-level-2.png \
- -n devnet \
- -t base-eth \
- -w 6dd5e....54a120201cb6a \
- --provider-url https://sepolia.base.org
-
-irys upload image-level-3.png \
- -n devnet \
- -t base-eth \
- -w 6dd5e....54a120201cb6a \
- --provider-url https://sepolia.base.org
-```
-
-## Uploading the metadata
-
-Create three metadata files similar to the ones below. Make sure to change the value of the image field to match the URLs generated in the previous step.
-
-```json filename="metadata-level-1.json"
-{
- "name": "SuperMon",
- "symbol": "SMON",
- "image": "https://gateway.irys.xyz/3JE8cucmpLkXK1t84QwqDRv25FTB2EJWCUgpWdtvuJZd",
- "description": "Super dooper, changing shapes, changing power",
- "attributes": [
- {
- "trait_type": "supermon-level",
- "value": "1"
- }
- ]
-}
-```
-
-```json filename="metadata-level-2.json"
-{
- "name": "SuperMon",
- "symbol": "SMON",
- "image": "https://gateway.irys.xyz/3JE8cucmpLkXK1t84QwqDRv25FTB2EJWCUgpWdtvuJZd",
- "description": "Super dooper, changing shapes, changing power",
- "attributes": [
- {
- "trait_type": "supermon-level",
- "value": "2"
- }
- ]
-}
-```
-
-```json filename="metadata-level-3.json"
-{
- "name": "SuperMon",
- "symbol": "SMON",
- "image": "https://gateway.irys.xyz/3JE8cucmpLkXK1t84QwqDRv25FTB2EJWCUgpWdtvuJZd",
- "description": "Super dooper, changing shapes, changing power",
- "attributes": [
- {
- "trait_type": "supermon-level",
- "value": "3"
- }
- ]
-}
-```
-
-And upload **just the first file** using the Irys CLI.
-
-```console
-irys upload metadata-level-1.json \
- -n devnet \
- -t base-eth \
- -w 6dd5e....54a120201cb6a \
- --provider-url https://sepolia.base.org
-```
-
-The CLI will return a URL similar to `https://gateway.irys.xyz/94TNg3UUKyZ96Dj8eSo9DVkBiivAz9jT39jjMFeTFvm3`. To convert that to a mutable-style URL, interpolate it by adding `/mutable/` after the domain and before the transaction ID.
-
-Your final URL will be similar to `https://gateway.irys.xyz/mutable/94TNg3UUKyZ96Dj8eSo9DVkBiivAz9jT39jjMFeTFvm3`.
-
-## Minting the NFT
-
-To mint your NFT in Remix:
-
-1. Return to Remix.
-2. Under "Deployed Contracts", locate your contract and expand it to see its functions.
-3. Under the `Mint` function, enter the wallet address you want to mint the NFT to and the metadata URL (e.g. `https://gateway.irys.xyz/mutable/94TNg3UUKyZ96Dj8eSo9DVkBiivAz9jT39jjMFeTFvm3`) from the previous step.
-4. Click Transact.
-
-
-
-
-
-You can now view the NFT on the [Opensea Testnet].
-
-## Mutating the metadata
-
-To now "mutate" the NFT, upload a new version of the metadata tagging it as having a `Root-TX` equal to the transaction ID of your first transaction. In my example, I pass the value of `94TNg3UUKyZ96Dj8eSo9DVkBiivAz9jT39jjMFeTFvm3`, however make sure to change this to match your unique transaction ID.
-
-```console
-irys upload metadata-level-2.json \
- -n devnet \
- -t base-eth \
- -w 6dd5e....54a120201cb6a \
- --tags Root-TX 94TNg3UUKyZ96Dj8eSo9DVkBiivAz9jT39jjMFeTFvm3 \
- --provider-url https://rpc.sepolia.org
-```
-
-Return to Opensea and request that it refresh your metadata.
-
-
-
-
-
-Give it a few minutes and your updated NFT should be visible.
-
-## Free metadata uploads
-
-Uploads of less than 100 KiB are free on Irys, which is more than enough for most metadata files. This means projects can let users "evolve" their NFTs without having to pay gas fees.
-
-## Caching
-
-Wallets and NFT websites typically cache metadata to optimize performance, this can affect the visibility of updates to dynamic NFTs. While OpenSea offers a feature for users to manually request metadata refreshes, not all platforms provide this level of control. When building dynamic NFT projects, make sure to thoroughly test and understand the implications of caching on your platform.
-
-## Irys SDK
-
-This tutorial used the Irys CLI to permanently upload data. Irys also has a JS-SDK that can be used on the [server] and in the [browser].
-
-## Conclusion
-
-In this tutorial, you learned how to permanently upload data to Irys using their CLI and how to create a dynamic NFT. Data on Irys is onchain, permanent and immutable. When your NFT images and metadata are on Irys, you can guarantee to your users that the NFT will be retrievable forever.
-
-Dynamic NFTs are commonly used with gaming projects, similar to the one we built in this tutorial. However, there are countless other applications too. For example:
-
-- **Points programs**: Create an NFT representing a user's participation in a points program. As the user earns more points, the NFT evolves.
-- **Sports NFTs**: Create an NFT from a sports team that changes whenever the team wins an important game.
-- **Holiday NFTs**: Create an NFT that changes seasonally to represent different holidays.
-- **Activity tracking**: Create a health and wellness NFT that changes based on data from an activity tracker.
-
-[Irys]: https://www.irys.xyz/
-[Base Faucet]: https://docs.base.org/docs/tools/network-faucets/
-[faucets]: https://docs.base.org/docs/tools/network-faucets/
-[Base Sepolia]: https://docs.base.org/docs/tools/network-faucets/
-[browser]: https://docs.irys.xyz/build/d/irys-in-the-browser
-[Coinbase Wallet]: https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=e
-[Download a zip containing PNGs]: https://gateway.irys.xyz/MoOvEzePMwFgc_v6Gw3U8ovV6ostgrkWb9tS4baAJhc
-[Irys CLI]: https://docs.irys.xyz/build/d/storage-cli/installation
-[mutability features]: https://docs.irys.xyz/build/d/features/mutability
-[Opensea Testnet]: https://testnets.opensea.io/account
-[Remix]: https://docs.base.org/tutorials/deploy-with-remix
-[server]: https://docs.irys.xyz/build/d/quickstart
-
-
diff --git a/docs/learn/token-development/nft-guides/signature-mint.mdx b/docs/learn/token-development/nft-guides/signature-mint.mdx
deleted file mode 100644
index 5e59dc165..000000000
--- a/docs/learn/token-development/nft-guides/signature-mint.mdx
+++ /dev/null
@@ -1,374 +0,0 @@
----
-title: 'Signature Mint NFT'
-slug: /signature-mint-nft
-description: A tutorial that teaches how to create a signature mint, in which minters pay their own gas, but must first be given a valid signed authorization.
-author: briandoyle81
-
----
-
-# Signature Mint NFT
-
-A _signature mint_ is a term for an NFT mint in which the recipient of the NFT pays for their own gas to receive the NFT, but may only do so if they possess a correct message signed by the owner or authorized address of the mint contract. Reasons for doing this include allowing fiat payment of minting fees, allowing holders of an NFT on one chain to mint that NFT on an unrelated chain, or gating the mint to users who meet other specific offchain requirements.
-
-Signature mints are not particularly complex, but they remain challenging to implement. Because they make use of both hashing and cryptography, there are no partially-correct states - either everything is exactly right and the mint works, or **something** is wrong **somewhere** and it doesn't.
-
-Combined with the rapid changes in Solidity, library contracts, and frontend libraries, troubleshooting errors is particularly difficult.
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Cryptographically sign a message with a wallet
-- Validate a signed message in a smart contract
-- Implement a signature ERC-721 mint
-
-## Prerequisites
-
-### ERC-721 Tokens
-
-This tutorial assumes that you are able to write, test, and deploy your own ERC-721 tokens using the Solidity programming language. If you need to learn that first, check out our content in [Base Learn] or the sections specific to [ERC-721 Tokens]!
-
-### Vercel
-
-You'll need to be comfortable deploying your app to [Vercel], or using another solution on your own. Check out our tutorial on [deploying with Vercel] if you need a refresher!
-
-## Building the Contract
-
-Start by setting up an [OpenZeppelin ERC-721] contract. Set up variables and use the constructor to assign:
-
-- A name for the collection
-- Symbol for the collection
-- Description
-- IPFS Hash for the NFT art (assuming the same art for each NFT)
-- A counter to keep track of which NFT id is next
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity 0.8.24;
-
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
-import "@openzeppelin/contracts/access/Ownable.sol";
-
-contract SoulboundSignatureMint is ERC721, Ownable {
- string public nameString;
- string public description;
- string public tokenArtIPFSId;
- uint public counter;
-
- constructor(
- string memory _nameString,
- string memory _symbol,
- string memory _tokenArtIPFSId,
- string memory _description
- ) ERC721(_nameString, _symbol) Ownable(msg.sender) {
- nameString = _nameString;
- description = _description;
- tokenArtIPFSId = _tokenArtIPFSId;
- }
-}
-```
-
-You're also using `Ownable` to assign an owner to this contract. You could instead just save an address for the authorized signer if you aren't going to add any functionality only the owner can invoke.
-
-### Public Mint Function
-
-For the `public`-facing mint function, create a function called `mintTo` that accepts an `address` for the `_recipient`.
-
-
-A common pattern used to be to simply mint the token and give it to `msg.sender`. This practice is falling out of favor. Allowing the recipient to be different than the sender gives greater flexibility. Doing so is also necessary to assign the right NFT owner in the event the user is using a smart contract wallet, paymaster, or other form of account abstraction.
-
-
-```solidity
-function mintTo(address _recipient, bytes memory _signature) public {
- counter++;
- _safeMint(msg.sender, counter);
-}
-```
-
-### Onchain Metadata
-
-Rather than pointing to a `json` file on the traditional internet, you can put your metadata directly in the contract. To do so, first import some helper libraries:
-
-```solidity
-import "@openzeppelin/contracts/utils/Base64.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-```
-
-Next, `override` the functions for `_baseURI` and `tokenURI` to return base 64 encoded json metadata with the information you supplied in the constructor:
-
-```solidity
-function _baseURI() internal pure override returns (string memory) {
- return "data:application/json;base64,";
-}
-
-function tokenURI(uint _tokenId) public view override returns (string memory) {
- if(_tokenId > counter) {
- revert InvalidTokenId(_tokenId);
- }
-
- string memory json = Base64.encode(
- bytes(
- string(
- abi.encodePacked(
- '{"name": "',
- nameString,
- ' #: ',
- Strings.toString(_tokenId),
- '","description": "',
- description,
- '", "image": "ipfs://',
- tokenArtIPFSId,
- '"}'
- )
- )
- )
- );
-
- return string(abi.encodePacked(_baseURI(), json));
-}
-```
-
-**Be very careful** setting up the single and double quotes above and be sure to test this function to make sure the result is valid json metadata. An error here will break the NFT and it won't show up correctly in wallets or marketplaces!
-
-### Preventing Transfers
-
-_Soulbound_ is a video-game term that means that an item is permanently attached to the receiver - it can **not** be transferred. It's up to you if this restriction fits your design goals. We use it often because our NFTs are intended to be fun mementos or markers of personal accomplishment and not something that will ever have monetary value. Preventing trading reduces speculation and farming on something we did for fun!
-
-To prevent transfers other than the initial mint, you can `override` the `_update` function.
-
-
-Previously, this was done with the `_beforeTransfer` function. Current versions of OpenZeppelin's ERC-721 implementation have replaced that function with `_update`.
-
-
-```solidity
-/**
- * Disallow transfers (Soulbound NFT)
- */
-/**
- * @dev Internal function to handle token transfers.
- * Restricts the transfer of Soulbound tokens.
- */
-function _update(address to, uint256 tokenId, address auth)
- internal
- override(ERC721)
- returns (address)
-{
- address from = _ownerOf(tokenId);
- if (from != address(0) && to != address(0)) {
- revert SoulboundToken();
- }
-
- return super._update(to, tokenId, auth);
-}
-```
-
-### Deploy and Test
-
-Before getting into the complexity of validating a cryptographic signature, it's a good idea to validate your contract and make sure it is working as expected. You'll need to pin an image to IPFS and get a hash for it to use in your metadata. You can use a service like [Pinata] to help with that.
-
-### Adding Signature Validation
-
-To validate the signature that you'll later create in your backend, you'll use a pair of cryptography utilities from OpenZeppelin:
-
-```solidity
-import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
-import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
-```
-
-```solidity
-using ECDSA for bytes32;
-using MessageHashUtils for bytes32;
-```
-
-These utilities abstract away most of the complexity involved in working with messages adhering to the [ERC-191] and [EIP-712] specifications. Importantly, they work with the message format that prefixes `"\x19Ethereum Signed Message:\n32"` to the message. You **must** also do this when creating the signed message!
-
-Add a function to `validateSignature`:
-
-```solidity
-function validateSignature(address _recipient, bytes memory _signature) public view returns (bool) {
- bytes32 messageHash = keccak256(abi.encodePacked(_recipient));
- bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash();
- address signer = ethSignedMessageHash.recover(_signature);
-
- return signer == owner();
-}
-```
-
-The way the verification works is a little obtuse, particularly given that you haven't created the `_signature` yet. The function has two inputs:
-
-- The message or variables in the signed message
- - Here, this is the `address` of the `_recipient`
- - The `_signature`, or signed message provided by the user claiming they have been given permission to mint the NFT
-
-First, the function recreates the hash of the data to be signed. If you were including other variables, you'd include them here as well. Next, `messageHash.toEthSignedMessageHash` prepends the bytes representation of `"\x19Ethereum Signed Message:\n32"` to the message, then hashes the result.
-
-Finally, calling `recover` with `ethSignedMessageHash` and the `_signature` attempts to recover the signing `address` from the `_signature` using the **independently constructed** message data.
-
-If the recovered address matches the expected address, in this case, the contract owner, then the provided `_signature` is valid. If the addresses do not match, then the `_signature` is not valid.
-
-Update your `mintTo` function to make use of the validation:
-
-```solidity
-function mintTo(address _recipient, bytes memory _signature) public {
- if(!validateSignature(_recipient, _signature)) {
- revert InvalidSignature();
- }
-
- counter++;
- _safeMint(msg.sender, counter);
-}
-```
-
-
-Nothing in the above validation method prevents a user, or a third party, from obtaining a valid, signed message from a previous transaction and reusing it for a **new** transaction. In this case, it doesn't matter because signature re-use would only allow minting more soulbound NFTs for the address within the signature.
-
-Other design requirements should use a nonce as a part of the signed data to prevent signature reuse.
-
-
-## Signing the Message
-
-If you're using [Hardhat] with [viem], you can write tests to verify the signing and validation mechanisms are working. Otherwise, there isn't a point, as success is dependent on the exact and specific way and order signing happens. If you're using a different toolkit to write your smart contracts, continue in your backend directly.
-
-If you're using a different library, you'll need to do research to figure out how to **exactly** reproduce the steps below.
-
-### Setting Up the Test
-
-Add a new test file and fill out a skeleton to deploy your contract and run a test:
-
-```tsx
-import { loadFixture } from '@nomicfoundation/hardhat-toolbox-viem/network-helpers';
-import { expect } from 'chai';
-import hre from 'hardhat';
-
-describe('Test', function () {
- // We define a fixture to reuse the same setup in every test.
- // We use loadFixture to run this setup once, snapshot that state,
- // and reset Hardhat Network to that snapshot in every test.
- async function deploySignatureFixture() {
- // Contracts are deployed using the first signer/account by default
- const [owner, signer0, signer1] = await hre.viem.getWalletClients();
-
- const soulboundSignatureMint = await hre.viem.deployContract('SoulboundSignatureMint', [
- 'Cool NFT Name', // Name
- 'CNFT', // Symbol
- 'QmRsQCyTEALYnHvBupFcs2ofzeeswEEEGN...', // IPFS Hash
- 'This is a cool NFT!', // Description
- ]);
-
- const publicClient = await hre.viem.getPublicClient();
-
- return {
- soulboundSignatureMint,
- owner,
- signer1,
- publicClient,
- };
- }
-
- describe('Mint', function () {
- it('Should validate the signed message', async function () {
- const { soulboundSignatureMint, owner, signer0, signer1 } = await loadFixture(
- deploySignatureFixture,
- );
-
- const ownerAddress = await owner.account.address;
- const signer1Address = await signer1.account.address;
-
- // TODO...
-
- // Signer 1 calls the mintTo function with the signature
- expect(await soulboundSignatureMint.write.mintTo([signer1Address, signature])).to.be.ok;
- });
- });
-});
-```
-
-You can use the example in the documentation for [signMessage] in the [viem] wallet client to get started, but it will **not** work as expected.
-
-```tsx
-// BAD CODE EXAMPLE DO NOT USE!
-const signature = await owner.signMessage({
- message: signer1Address,
-});
-```
-
-Try it, and it will fail. Add a log to your contract and you'll see that the recovered `signer` address is random, rather than the first address in the list of default Hardhat accounts.
-
-The reason for this is that while `signMessage` does follow the previously mentioned standards, prepends `"\x19Ethereum Signed Message:\n32"` to the message, and correctly signs it, it does **not** prepare the data to be signed in exactly the same way as the smart contract converts the `address` to `bytes32`.
-
-To fix this, first import some helper functions from [viem]:
-
-```tsx
-import { keccak256, encodePacked, toBytes } from 'viem';
-```
-
-Then `encodePacked` and `keccak256` hash your variables and turn them into `bytes`, just like you did in the contract in `validateSignature`:
-
-```tsx
-const message = keccak256(encodePacked(['address'], [signer1Address]));
-const messageBytes = toBytes(message);
-```
-
-Finally, call the wallet `signMessage` function with the newly assembled `messageBytes`. You'll need to mark the data representation as `raw`:
-
-```tsx
-const signature = await owner.signMessage({
- message: { raw: messageBytes },
-});
-```
-
-Test again, and it will pass!
-
-## Signing from the Backend
-
-It's up to you to determine the conditions that you're willing to sign a message. Once those conditions are met, you can use a similar process to load a wallet from your private key and sign the message on any TypeScript backend:
-
-```tsx
-const authorizedAccount = privateKeyToAccount(COINBASE_WALLET_KEY as `0x${string}`);
-
-const authorizedClient = createWalletClient({
- account: authorizedAccount,
- chain: base,
- transport: http(), // Leave empty for local account
-});
-
-// Align signed message with OpenZeppelin/Solidity
-
-const messageToSign = keccak256(encodePacked(['address'], [userAddress as `0x${string}`]));
-const messageBytes = getBytes(messageToSign);
-
-// Create an Ethereum Signed Message with the user's address
-const signedMessage = await authorizedClient.signMessage({
- message: { raw: messageBytes },
-});
-console.log('User address:', userAddress);
-console.log('Signed message:', signedMessage);
-
-const data = encodeFunctionData({
- abi: mintContractData.abi,
- functionName: 'mintTo',
- args: [userAddress, signedMessage],
-});
-```
-
-
-`privateKeyToAccount` expects that your key starts with `0x`. You may need to manually add that depending on the tool you exported it from.
-
-
-## Conclusion
-
-In this tutorial, you've learned how to create a signature mint, which allows you to set conditions on a backend before a user is allowed to mint your NFT. You've learned the detailed and specific steps needed to align [viem]'s method of signing messages with [OpenZeppelin]'s method of verifying them. Finally, you've learned a few of the risks and considerations needed in designing this type of mint.
-
-[Base Learn]: https://base.org/learn
-[ERC-721 Tokens]: https://docs.base.org/base-learn/docs/erc-721-token/erc-721-standard-video
-[Vercel]: https://vercel.com
-[deploying with Vercel]: /tutorials/farcaster-frames-deploy-to-vercel
-[OpenZeppelin ERC-721]: https://docs.openzeppelin.com/contracts/2.x/api/token/erc721
-[Pinata]: https://www.pinata.cloud/
-[ERC-191]: https://eips.ethereum.org/EIPS/eip-191
-[EIP-712]: https://eips.ethereum.org/EIPS/eip-712
-[Hardhat]: https://hardhat.org/
-[viem]: https://viem.sh/
-[signMessage]: https://viem.sh/docs/actions/wallet/signMessage.html
-[OpenZeppelin]: https://www.openzeppelin.com/
-
diff --git a/docs/learn/token-development/nft-guides/simple-onchain-nfts.mdx b/docs/learn/token-development/nft-guides/simple-onchain-nfts.mdx
deleted file mode 100644
index 0b91ad887..000000000
--- a/docs/learn/token-development/nft-guides/simple-onchain-nfts.mdx
+++ /dev/null
@@ -1,1044 +0,0 @@
----
-title: 'Simple Onchain NFTs'
-slug: /simple-onchain-nfts
-description: A tutorial that teaches how to make simple nfts that are procedurally generated and have onchain metadata and images.
-author: briandoyle81
----
-
-# Simple Onchain NFTs
-
-Many NFTs are dependent on offchain metadata and images. Some use immutable storage locations, such as [IPFS]. Others use traditional web locations, and many of these allow the owner of the contract to modify the URL returned by a contract when a site or user attempts to retrieve the location of the token art and metadata. This power isn't inherently bad, because we probably want someone to be able to fix the contract if the storage location goes down. However, it does introduce a requirement to trust the contract owner.
-
-In this tutorial, we'll show you how to do this to create a simple NFT that is fully onchain. This contract is used in our tutorials for [Thirdweb and Unreal - NFT Items] and the [Coinbase Smart Wallet].
-
-The result of this tutorial is used in other tutorials. [Below], you can find the complete contract and ABI. Feel free to use it if you're working on one of those and don't want to get sidetracked.
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Programmatically generate and return json metadata for ERC-721 tokens
-- Deterministically construct unique SVG art in a smart contract
-- Generate deterministic, pseudorandom numbers
-
-## Prerequisites
-
-### ERC-721 Tokens
-
-This tutorial assumes that you are able to write, test, and deploy your own ERC-721 tokens using the Solidity programming language. If you need to learn that first, check out our content in [Base Camp] or the sections specific to [ERC-721 Tokens]!
-
-### Vector Art
-
-You'll need some familiarity with the SVG art format and a basic level of ability to edit and manipulate vector art. If you don't have this, find an artist friend and collaborate!
-
-## Building the Contract
-
-Start by setting up an [OpenZeppelin ERC-721] contract. You'll need to set up a `mintTo` function that accepts the address that should receive the NFT.
-
-```solidity filename="RandomColorNFT.sol"
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.24;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
-
-contract RandomColorNFT is ERC721 {
- uint public counter;
-
- constructor() ERC721("RandomColorNFT", "RCNFT") {
- }
-
- function mintTo(address _to) public {
- counter++;
- _safeMint(_to, counter);
- }
-}
-```
-
-
-With the Smart Wallet, `msg.sender` is the users custodial address - where you want to send the NFT. This is not always the case with account abstraction. In some other implementations, `msg.sender` is the smart contract address, even if the user signs in with an EOA. Regardless, it's becoming a common practice to pass the address you want the NFT to go to explicitly.
-
-
-### Onchain Metadata
-
-Rather than pointing to a `json` file on the traditional internet, you can put your metadata directly in the contract. To do so, first import some helper libraries:
-
-```solidity
-import "@openzeppelin/contracts/utils/Base64.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-```
-
-Next, `override` the functions for `_baseURI` and `tokenURI` to return base 64 encoded json metadata with the appropriate information:
-
-```solidity
-function _baseURI() internal pure override returns (string memory) {
- return "data:application/json;base64,";
-}
-
-function tokenURI(uint _tokenId) public view override returns (string memory) {
- if(_tokenId > counter) {
- revert InvalidTokenId(_tokenId);
- }
-
- string memory json = Base64.encode(
- bytes(
- string(
- abi.encodePacked(
- '{"name": "',
- name(),
- ' #: ',
- Strings.toString(_tokenId),
- '","description": "Random colors are pretty or boring!", "image": "data:image/svg+xml;base64,',
- // TODO...,
- '"}'
- )
- )
- )
- );
-
- return string(abi.encodePacked(_baseURI(), json));
-}
-```
-
-**Be very careful** setting up the single and double quotes above and be sure to test this function to make sure the result is valid json metadata. An error here will break the NFT and it won't show up correctly in wallets or marketplaces!
-
-### Onchain SVG Image
-
-For this NFT, the art will consist of a simple onchain SVG containing a square with a pseudo-randomly chosen color. Check out our tutorial on [Building Onchain NFTs] if you want to try something more complicated.
-
-Start by scaffolding out a `render` function:
-
-```solidity
-function render(uint _tokenId) public view returns (string memory) {
- return string(
- abi.encodePacked(
- "",
- // TODO: add a rectangle with a random color fill
- ""
- )
- );
-}
-```
-
-Rectangles in SVG images are created with the [rect] element. To cover the whole background, you can set the width and height to the size of the `viewbox`. Although not listed directly in the MDN page for rectangles, you can add a `fill` property to add a fill color to any SVG element. You can use color names, or hex codes for colors:
-
-```html
-
-```
-
-### Generating a Random Color
-
-Instead of a fixed color, your design calls for a unique color for each NFT. Add a function to generate this:
-
-```solidity filename="RandomColorNFT.sol"
-// Function to generate a random color hex code
-function generateRandomColor() public view returns (string memory) {
- // Generate a pseudo-random number using block.prevrandao
- uint256 randomNum = uint256(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, msg.sender)));
-
- // Extract RGB components from the random number
- bytes memory colorBytes = new bytes(3);
- colorBytes[0] = bytes1(uint8(randomNum >> 16));
- colorBytes[1] = bytes1(uint8(randomNum >> 8));
- colorBytes[2] = bytes1(uint8(randomNum));
-
- // Convert RGB components to hex string
- string memory colorHex = string(abi.encodePacked(
- "#",
- toHexDigit(uint8(colorBytes[0]) >> 4),
- toHexDigit(uint8(colorBytes[0]) & 0x0f),
- toHexDigit(uint8(colorBytes[1]) >> 4),
- toHexDigit(uint8(colorBytes[1]) & 0x0f),
- toHexDigit(uint8(colorBytes[2]) >> 4),
- toHexDigit(uint8(colorBytes[2]) & 0x0f)
- ));
-
- return colorHex;
-}
-
-// Helper function to convert a uint8 to a hex character
-function toHexDigit(uint8 d) internal pure returns (bytes1) {
- if (d < 10) {
- return bytes1(uint8(bytes1('0')) + d);
- } else {
- return bytes1(uint8(bytes1('a')) + d - 10);
- }
-}
-```
-
-
-Remember, randomness generated using onchain information is not fully secure. A determined attacker could manipulate a block to compromise your contract. That being said, `prevrandao` is a passable solution for anything not involving a large amount of money.
-
-
-### Saving the Color to the NFT
-
-You'll need to generate this color with the function, then save it in a way that it can be retrieved when the `tokenURI` function is called. Add a mapping to store this relationship:
-
-```solidity
-mapping (uint => string) public tokenIdToColor;
-```
-
-Then set the color when the token is minted:
-
-```solidity
-function mintTo(address _to) public {
- counter++;
- _safeMint(_to, counter);
- tokenIdToColor[counter] = generateRandomColor();
-}
-```
-
-### Finishing the `tokenURI` Function
-
-Update your `render` function to generate the SVG.
-
-```solidity
-function render(uint _tokenId) public view returns (string memory) {
- return string(
- abi.encodePacked(
- "",
- "",
- ""
- )
- );
-}
-```
-
-Then update your `tokenURI` function to use it, and return the SVG as base64 encoded data:
-
-```solidity
-function tokenURI(uint _tokenId) public view override returns (string memory) {
- if(_tokenId > counter) {
- revert InvalidTokenId(_tokenId);
- }
-
- string memory json = Base64.encode(
- bytes(
- string(
- abi.encodePacked(
- '{"name": "',
- name(),
- ' #: ',
- Strings.toString(_tokenId),
- '","description": "Random colors are pretty or boring!", "image": "data:image/svg+xml;base64,',
- Base64.encode(bytes(render(_tokenId))),
- '"}'
- )
- )
- )
- );
-
- return string(abi.encodePacked(_baseURI(), json));
-}
-```
-
-### List of NFTs Owned
-
-Most ERC-721 implementations don't contain an on-contract method to retrieve a list of **all** the NFTs owned by a single address. The reason for this is that it costs extra gas go manage this list, and the information can be retrieved by using read-only services that analyze blockchain data.
-
-However, gas prices are getting lower, and adding this data to your contract will reduce your dependency on third-party APIs.
-
-To track ownership in-contract, first import `EnumerableSet` from OpenZeppelin:
-
-```solidity
-import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
-```
-
-Then enable it for `uint` sets and add a mapping to relate `addresses` to token ids.
-
-```solidity
-// Inside the RandomColorNFT contract
-using EnumerableSet for EnumerableSet.UintSet;
-
-mapping (address => EnumerableSet.UintSet) tokensOwned;
-```
-
-Finally, utilize the `_update` function to handle changes of ownership, including minting:
-
-```solidity
-function _update(address to, uint256 tokenId, address auth) internal override(ERC721) returns(address) {
- // Only remove the token if it is not being minted
- if (tokenId != counter){
- tokensOwned[auth].remove(tokenId);
- }
- tokensOwned[to].add(tokenId);
-
- return super._update(to, tokenId, auth);
-}
-```
-
-Now that you have a list of NFTs owned by an address, you can add a function to retrieve all of them. While you're at it, add the json metadata for each token. Doing so lets you get the complete list of NFTs **and** their metadata for just one RPC call!
-
-```solidity
-function getNFftsOwned(address owner) public view returns (TokenAndMetatdata[] memory) {
- TokenAndMetatdata[] memory tokens = new TokenAndMetatdata[](tokensOwned[owner].length());
- for (uint i = 0; i < tokensOwned[owner].length(); i++) {
- uint tokenId = tokensOwned[owner].at(i);
- tokens[i] = TokenAndMetatdata(tokenId, tokenURI(tokenId));
- }
- return tokens;
-}
-```
-
-### Testing
-
-Write some local tests, then [deploy] and test your contract. It can be very tricky to get all the commas, brackets, and single, and double quotes all lined up properly. The surest way to make sure it is working is to check the collection on [Testnet Opensea] or similar.
-
-Remember, it can take a few minutes for them to register and add the collection. If the metadata or image don't show up correctly, use [Sepolia Basescan] to pull the `tokenURI` and an online or console base64 decoder to decode and check the json metadata and SVG image.
-
-
-
-
-
-## Conclusion
-
-In this lesson, you learned how to make a simple NFT that is entirely onchain. You generated an SVG with a random color, and set up the JSON metadata for your NFT -- entirely onchain! Next, check out our tutorial for [Complex Onchain NFTs]!
-
-## Random Color NFT Contract
-
-```solidity filename="RandomColorNFT.sol"
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.24;
-
-import "hardhat/console.sol";
-import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
-import "@openzeppelin/contracts/utils/Base64.sol";
-import "@openzeppelin/contracts/utils/Strings.sol";
-import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
-
-contract RandomColorNFT is ERC721 {
- using EnumerableSet for EnumerableSet.UintSet;
-
- mapping (address => EnumerableSet.UintSet) tokensOwned;
-
- uint public counter;
-
- mapping (uint => string) public tokenIdToColor;
-
- error InvalidTokenId(uint tokenId);
- error OnlyOwner(address);
-
- constructor() ERC721("RandomColorNFT", "RCNFT") {
- }
-
- function mintTo(address _to) public {
- counter++;
- _safeMint(_to, counter);
- tokenIdToColor[counter] = generateRandomColor();
- }
-
- struct TokenAndMetatdata {
- uint tokenId;
- string metadata;
- }
-
- function getNftsOwned(address owner) public view returns (TokenAndMetatdata[] memory) {
- TokenAndMetatdata[] memory tokens = new TokenAndMetatdata[](tokensOwned[owner].length());
- for (uint i = 0; i < tokensOwned[owner].length(); i++) {
- uint tokenId = tokensOwned[owner].at(i);
- tokens[i] = TokenAndMetatdata(tokenId, tokenURI(tokenId));
- }
- return tokens;
- }
-
- function shuffleColor(uint _tokenId) public {
- if(_tokenId > counter) {
- revert InvalidTokenId(_tokenId);
- }
- if(ownerOf(_tokenId) != msg.sender) {
- revert OnlyOwner(msg.sender);
- }
- tokenIdToColor[_tokenId] = generateRandomColor();
- }
-
- function _update(address to, uint256 tokenId, address auth) internal override(ERC721) returns(address) {
- // Only remove the token if it is not being minted
- if (tokenId != counter){
- tokensOwned[auth].remove(tokenId);
- }
- tokensOwned[to].add(tokenId);
-
- return super._update(to, tokenId, auth);
- }
-
- function _baseURI() internal pure override returns (string memory) {
- return "data:application/json;base64,";
- }
-
- function tokenURI(uint _tokenId) public view override returns (string memory) {
- if(_tokenId > counter) {
- revert InvalidTokenId(_tokenId);
- }
-
- string memory json = Base64.encode(
- bytes(
- string(
- abi.encodePacked(
- '{"name": "',
- name(),
- ' #: ',
- Strings.toString(_tokenId),
- '","description": "Random colors are pretty or boring!", "image": "data:image/svg+xml;base64,',
- Base64.encode(bytes(render(_tokenId))),
- '"}'
- )
- )
- )
- );
-
- return string(abi.encodePacked(_baseURI(), json));
- }
-
- function render(uint _tokenId) public view returns (string memory) {
- return string(
- abi.encodePacked(
- "",
- "",
- ""
- )
- );
- }
-
- // Function to generate a random color hex code
- function generateRandomColor() public view returns (string memory) {
- // Generate a pseudo-random number using block.prevrandao
- uint256 randomNum = uint256(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, msg.sender)));
-
- // Extract RGB components from the random number
- bytes memory colorBytes = new bytes(3);
- colorBytes[0] = bytes1(uint8(randomNum >> 16));
- colorBytes[1] = bytes1(uint8(randomNum >> 8));
- colorBytes[2] = bytes1(uint8(randomNum));
-
- // Convert RGB components to hex string
- string memory colorHex = string(abi.encodePacked(
- "#",
- toHexDigit(uint8(colorBytes[0]) >> 4),
- toHexDigit(uint8(colorBytes[0]) & 0x0f),
- toHexDigit(uint8(colorBytes[1]) >> 4),
- toHexDigit(uint8(colorBytes[1]) & 0x0f),
- toHexDigit(uint8(colorBytes[2]) >> 4),
- toHexDigit(uint8(colorBytes[2]) & 0x0f)
- ));
-
- return colorHex;
- }
-
- // Helper function to convert a uint8 to a hex character
- function toHexDigit(uint8 d) internal pure returns (bytes1) {
- if (d < 10) {
- return bytes1(uint8(bytes1('0')) + d);
- } else {
- return bytes1(uint8(bytes1('a')) + d - 10);
- }
- }
-}
-```
-
-```json
-{
- "address": "0x59c35beE5eAdeEDDc2c34d419459243bD70AFD72",
- "abi": [
- {
- "inputs": [],
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "sender",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- },
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- }
- ],
- "name": "ERC721IncorrectOwner",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "ERC721InsufficientApproval",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "approver",
- "type": "address"
- }
- ],
- "name": "ERC721InvalidApprover",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "operator",
- "type": "address"
- }
- ],
- "name": "ERC721InvalidOperator",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- }
- ],
- "name": "ERC721InvalidOwner",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "receiver",
- "type": "address"
- }
- ],
- "name": "ERC721InvalidReceiver",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "sender",
- "type": "address"
- }
- ],
- "name": "ERC721InvalidSender",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "ERC721NonexistentToken",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "InvalidTokenId",
- "type": "error"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "name": "OnlyOwner",
- "type": "error"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "approved",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "approved",
- "type": "bool"
- }
- ],
- "name": "ApprovalForAll",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "approve",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- }
- ],
- "name": "balanceOf",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "counter",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "generateRandomColor",
- "outputs": [
- {
- "internalType": "string",
- "name": "",
- "type": "string"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "getApproved",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- }
- ],
- "name": "getNFftsOwned",
- "outputs": [
- {
- "components": [
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- },
- {
- "internalType": "string",
- "name": "metadata",
- "type": "string"
- }
- ],
- "internalType": "struct RandomColorNFT.TokenAndMetatdata[]",
- "name": "",
- "type": "tuple[]"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "operator",
- "type": "address"
- }
- ],
- "name": "isApprovedForAll",
- "outputs": [
- {
- "internalType": "bool",
- "name": "",
- "type": "bool"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "_to",
- "type": "address"
- }
- ],
- "name": "mintTo",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [
- {
- "internalType": "string",
- "name": "",
- "type": "string"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "ownerOf",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "_tokenId",
- "type": "uint256"
- }
- ],
- "name": "render",
- "outputs": [
- {
- "internalType": "string",
- "name": "",
- "type": "string"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- },
- {
- "internalType": "bytes",
- "name": "data",
- "type": "bytes"
- }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "internalType": "bool",
- "name": "approved",
- "type": "bool"
- }
- ],
- "name": "setApprovalForAll",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "_tokenId",
- "type": "uint256"
- }
- ],
- "name": "shuffleColor",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "bytes4",
- "name": "interfaceId",
- "type": "bytes4"
- }
- ],
- "name": "supportsInterface",
- "outputs": [
- {
- "internalType": "bool",
- "name": "",
- "type": "bool"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [
- {
- "internalType": "string",
- "name": "",
- "type": "string"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "name": "tokenIdToColor",
- "outputs": [
- {
- "internalType": "string",
- "name": "",
- "type": "string"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "_tokenId",
- "type": "uint256"
- }
- ],
- "name": "tokenURI",
- "outputs": [
- {
- "internalType": "string",
- "name": "",
- "type": "string"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "transferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
- ]
-}
-```
-
-[Base Camp]: https://base.org.camp
-[ERC-721 Tokens]: https://docs.base.org/base-camp/docs/erc-721-token/erc-721-standard-video
-[IPFS]: https://ipfs.tech/
-[Base64]: https://en.wikipedia.org/wiki/Base64
-[Hardhat and Hardhat Deploy]: https://docs.base.org/base-camp/docs/hardhat-deploy/hardhat-deploy-sbs
-[testnet version of Opensea]: https://testnets.opensea.io/
-[sample project]: https://github.com/base-org/land-sea-and-sky
-[Sample Art]: https://github.com/base-org/land-sea-and-sky/tree/master/Final_SVGs
-[Basescan]: https://sepolia.basescan.org/
-[Thirdweb and Unreal - NFT Items]: ./thirdweb-unreal-nft-items
-[Coinbase Smart Wallet]: ./coinbase-smart-wallet
-[Below]: #random-color-nft-contract
-[Complex Onchain NFTs]: ./complex-onchain-nfts
-
diff --git a/docs/learn/token-development/nft-guides/thirdweb-unreal-nft-items.mdx b/docs/learn/token-development/nft-guides/thirdweb-unreal-nft-items.mdx
deleted file mode 100644
index 5a2841112..000000000
--- a/docs/learn/token-development/nft-guides/thirdweb-unreal-nft-items.mdx
+++ /dev/null
@@ -1,662 +0,0 @@
----
-title: 'Thirdweb and Unreal - NFT Items'
-slug: /thirdweb-unreal-nft-items
-description: Learn how to use NFTs as in-game items using Thirdweb and Unreal.
-author: briandoyle81
----
-
-# Thirdweb and Unreal - NFT Items
-
-[thirdweb] provides a number of contracts and tools for building onchain. Their [Gaming SDK] enables seamless onboarding, cross-platform support, and provides many other features. It's compatible with [Unreal Engine] and can be used to enable onchain elements in your games.
-
-In this tutorial, you'll learn how to add NFT item usage on top of the demo game you build in their [Unreal Engine Quickstart]. Specifically, you'll use an NFT collection of random colors to change the color of the player's race car.
-
-
-
-
-
-## Objectives
-
-By the end of this tutorial you should be able to:
-
-- Pull a user's NFTs into an Unreal Engine Game
-- Apply elements from the NFT to game entities
-- Award NFTs to players for game accomplishments or actions
-
-## Prerequisites
-
-### ERC-721 Tokens
-
-This tutorial assumes that you are able to write, test, and deploy your own ERC-721 tokens using the Solidity programming language. If you need to learn that first, check out our content in [Base Learn] or the sections specific to [ERC-721 Tokens]!
-
-### Unreal Engine
-
-This tutorial will cover everything you need to know to accomplish the learning objectives, but it won't teach you how to make a game. You'll need to take further steps to learn the [Unreal Engine] on your own. You'll also need to have Visual Studio or Visual Studio Code set up to edit and compile Unreal files.
-
-### Onchain Apps
-
-The tutorial assumes you're comfortable with the basics of deploying an app and connecting it to a smart contract. If you're still learning this part, check out our tutorials in [Base Learn] for [Building an Onchain App].
-
-## Reviewing the Contract
-
-In our tutorial for building a [Simple Onchain NFTs], you can find an example of an ERC-721 NFT contract. It's an extension of the [OpenZeppelin ERC-721] implementation. When a user mints, they're granted an NFT with a random color. The metadata is fully onchain, as is the svg image. The image is a simple 1024\*1024 `rect`, with a `fill` of the randomly generated color.
-
-If the user dislikes the color, they may shuffle it and the NFT will change to a new randomly-selected color.
-
-These NFTs are not restricted for trading. The contract includes a utility function, `getNftsOwned`, which will return an array containing the `tokenId` and base64-encoded metadata string for all tokens currently owned by the provided `address`.
-
-## Getting Started with Unreal
-
-Continue below for some tips on completing the [Unreal Engine Quickstart] tutorial provided by thirdweb. This tutorial will guide you through installing the Unreal Engine and setting up the major components of the thirdweb [Gaming SDK].
-
-It will also guide you through setting up the website and backend that are needed to support the game integration. The client in their example uses Next.js, and the server is built with Node and Express.
-
-### Setting up the Engine
-
-First, you need to set up the [Engine]. For testing, you can [run it locally] with a [Docker] container.
-
-If you need to, install or update [Docker] and [Postgres].
-
-Start Postgres:
-
-```shell
-docker run -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres
-```
-
-Make sure Docker desktop is running.
-
-Create a [thirdweb API key]. Allow `localhost:3000` and `localhost:8000` when creating your api key. When you deploy, you'll need to update the allowed domains.
-
-The command to launch the engine itself is complicated and has many parameters. You'll want to create a file and run it from that. Create `thirdweb-engine.sh` in a convenient location and add:
-
-```shell
-docker run \
- -e ENCRYPTION_PASSWORD="" \
- -e THIRDWEB_API_SECRET_KEY="" \
- -e ADMIN_WALLET_ADDRESS="" \
- -e POSTGRES_CONNECTION_URL="postgresql://postgres:postgres@host.docker.internal:5432/postgres?sslmode=disable" \
- -e ENABLE_HTTPS=true \
- -p 3005:3005 \
- --pull=always \
- --cpus="0.5" \
- thirdweb/engine:latest
-```
-
-Enter your `THIRDWEB_API_SECRET_KEY` and the wallet address you sign into thirdweb with as the `ADMIN_WALLET_ADDRESS`. You can see a full list of [environment variables] in the docs, but shouldn't need to set any others now.
-
-Give your script permission to run with `chmod +x ./thirdweb-engine.sh` then run it with `./thirdweb-engine.sh
-`.
-
-It will take awhile to spin up, and you can ignore the warning about the `Chain Indexer Listener not started...`.
-
-Once the engine is running, navigate to `https://localhost:3005/json`. Click through the warning that the connection is not secure. Doing so allows your browser to connect to your engine instance.
-
-
-
-We do not have an official browser recommendation, but during our testing, Chrome worked and Brave did not with the engine setup and configuration.
-
-
-
-Navigate to the [thirdweb engine dashboard], and click the `Import` button. Enter a name and the local address for your engine instance:
-
-
-
-
-
-Next, you must add your wallet to the engine instance. Open up the instance in the dashboard, then click the `Import` button next to `Backend Wallets`. Enter your secret key for the wallet.
-
-
-
-Remember, the wallet key gives full access to all assets within any wallet. Use separate wallets for development and individual production tasks. Don't hold or fund a production wallet with any assets other than the minimum amount necessary for the task it is accomplishing.
-
-
-
-**Be sure to fund** this wallet with Base Sepolia ETH. It will be paying the gas for transactions.
-
-
-
-The key to your wallet is stored in ephemeral memory in the engine itself. You'll need to re-add it whenever you restart the engine.
-
-
-
-## Setting up the Client and Server
-
-Clone the [engine-express] repo. CD into the `client` and `server` repos and run `yarn` to install dependencies, then CD back to root and run `yarn` again.
-
-In both the `client` and `server` folders, copy or rename the `.env.example` files as `.env`.
-
-### Client
-
-In the client `.env`:
-
-- Set `NEXT_PUBLIC_THIRDWEB_CLIENT_ID` to the **Client ID** matching your [thirdweb API key]
-- You don't need to change the `NEXT_PUBLIC_BACKEND_URL`
-- Set the `NEXT_PUBLIC_THIRDWEB_AUTH_DOMAIN` as `localhost`
-
-In the server `.env`:
-
-- Don't change the `THIRDWEB_ENGINE_URL`. (It **is** supposed to be `https`)
-- Set the `THIRDWEB_ENGINE_BACKEND_WALLET` to the same as you used in the engine setup
-- Set the `THIRDWEB_AUTH_DOMAIN` as `localhost`
-- Set the `THIRDWEB_API_SECRET_KEY` to the **Secret Key** matching your [thirdweb API key]
-- Set the `THIRDWEB_AUTH_PRIVATE_KEY` to the private key matching your backend engine wallet
-
-Open `client/components/ThirdwebProvider.tsx`. Update the `activeChain` to Base Sepolia.
-
-```tsx
-import { BaseSepoliaTestnet } from '@thirdweb-dev/chains';
-
-// This is the chainId your dApp will work on.
-const activeChain = BaseSepoliaTestnet;
-```
-
-### Server
-
-Open `server/src/controllers/engineController.ts`. Update the `const`s at the beginning to load from environment variables:
-
-```tsx
-const ENGINE_URL = process.env.THIRDWEB_ENGINE_URL;
-const BACKEND_WALLET = process.env.THIRDWEB_ENGINE_BACKEND_WALLET;
-const ERC20_CONTRACT = process.env.ERC20_CONTRACT;
-const CHAIN = process.env.CHAIN;
-```
-
-You'll need to deploy your own version of the [Token Drop] contract. Click `Deploy Now`, then enter the name, symbol, and image of your choosing.
-
-**Select `Base Sepolia Testnet` as the Network / Chain**.
-
-You can leave the `Recipient Address` as your connected address, and you don't need to do an advanced configuration.
-
-Click `Deploy Now`, and approve the transactions to deploy the contract and add it to your dashboard.
-
-Next, click the `Claim Conditions` tab on the left side nav. Then click the `+ Add Phase` button and select `Public`. Review the options, but don't change any of them for this demo. Click `Save Phases`.
-
-
-
-If later in the tutorial, you get an error when you attempt to claim a token, but not an error every four seconds failing to read the balance, it's because you missed this step.
-
-
-
-Copy the address from the dashboard:
-
-
-
-
-
-Return to the `.env` for your server, and add:
-
-```env
-ERC20_CONTRACT=0x... # Your Address
-CHAIN=84532 # Base Sepolia
-```
-
-Run the client and server with `yarn client` and `yarn server`. Navigate to `localhost:3000`, create a user, and link a wallet.
-
-## Setting Up the Game
-
-Clone the thirdweb [Unreal Demo], and open it with the Unreal Editor. Do so by clicking the `Recent Projects` tab in the upper left, then `Browse`, in the lower right.
-
-
-
-
-
-Open the folder cloned from the repo and select `unreal_demo.uproject`. You may need to convert the project to the current version of Unreal. Click the `Open a copy` button.
-
-When the scene loads, double-click `Scene_Game` in the upper-right corner.
-
-
-
-
-
-Before you can play, you need to do some config. Scroll down in the `Outliner` until you find `ThirdWebManager`. Click the `Open Thirdweb Manager` button to open the file in your editor.
-
-
-
-
-
-Then, click the green play button at the top of the viewport.
-
-
-
-
-
-Log in using the credentials you created on the website, and play the game for a minute or two. If you get a 404, check that your engine, client, and server are all still running.
-
-
-
-The demo does not actually have a database connected for users. You'll need to recreate your user each time you restart the server. For production, you'll need to swap this out with an actual database.
-
-
-
-If you get an error 500 `"No configured wallet found with address 0xABCD...."`, it's because you didn't add your wallet in the [thirdweb engine dashboard].
-
-Otherwise, the game should run, and you should receive an ERC20 NFT every time you collect one of the orange orbs on the race track.
-
-## Adding the Color Changer
-
-Your next goal is to make it so that your players can use their Random Color NFTs as skins on the race car. You'll need to deploy the [contract provided below], set it up to be accessed via the server and engine, and finally, enable the colors from the NFTs to be used to change the color of the car.
-
-### Deploying the Contract
-
-You'll use thirdweb's platform to deploy this contract as well. Open a new folder in your editor and run:
-
-```shell
-npx thirdweb create contract
-```
-
-Then:
-
-- Name the project - `random-color-nft`, or `.` if you run the script from the folder where you want the project
-- Select your preference of `Forge` or `Hardhat`
-- Name the NFT contract - `RandomColorNFT`
-- Select `Empty Contract`
-
-Open `contracts/Contract.sol` and replace the contents with the [contract provided below].
-
-You'll need to import or install the OpenZeppelin contracts. You may also need to update the config for the development environment you're using to `0.8.24`.
-
-Run `yarn build`.
-
-Select `y` to install the thirdweb package and wait for the script to complete.
-
-Run `yarn deploy`.
-
-If you haven't linked your device to your thirdweb account, the browser will open to a page asking you to make the connection. Do so now.
-
-After the script runs for a moment, it will open the thirdweb dashboard with the deployment UI open. Select `Base Sepolia Testnet` as your network, then click the `Deploy Now` button. Sign the transaction and wait for the contract to deploy.
-
-### Adding the Contract to the Server
-
-Copy the address for the contract to the clipboard and return to `thirdweb-engine-express`. Open `server/.env` and add:
-
-```env
-RANDOM_COLOR_NFT_CONTRACT=
-```
-
-Open `server/src/controllers/engineController.ts` and add it there as well:
-
-```tsx
-const RANDOM_COLOR_NFT_CONTRACT = process.env.RANDOM_COLOR_NFT_CONTRACT;
-```
-
-Now, using `claimERC20` as a template, add a function to `claimRandomColorNFT`. It's identical, except the `url`, `body`, and error message are:
-
-```tsx
-// Other code...
-const url = `${ENGINE_URL}/contract/${CHAIN}/${RANDOM_COLOR_NFT_CONTRACT}/write`;
-// Other code
-const body = {
- functionName: 'mintTo',
- args: [user.ethAddress],
-};
-// Other code
-res.status(400).json({ message: 'Error claiming RandomColorNFT' });
-```
-
-
-
-A better practice for production would be to make a more generalized function that can handle multiple requests to your contracts. We're skipping that for now to avoid needing to refactor the existing collectibles in the game.
-
-
-
-Next, you need to add a route for this function. Open `server/src/routes/engineRoutes.ts`. Import `claimRandomColorNFT` and add a route for it:
-
-```tsx
-router.post('/claim-random-color-nft', claimRandomColorNFT);
-```
-
-### Collecting the NFT from the Game
-
-Return to the Unreal Editor and open `ThirdwebManager.cpp`:
-
-
-.
-
-
-Similarly to what you did in the server, use the existing `PerformClaim()` as a template to add a function for `PerformNFTClaim()`. The only thing different is the name of the function and the URL:
-
-```c++
-HttpRequest->SetURL(this->ServerUrl + "/engine/claim-random-color-nft");
-```
-
-
-
-Again, it would be better practice to generalize this function, but you can skip that for now to avoid needing to update all the collectibles.
-
-
-
-Next, you need to let the editor know about this new function. Open `Source/unreal_demo/Public/ThirdwebManager.h`. Add your new function under the one for `PerformClaim();`
-
-```c++
-// Function to perform the NFT claim operation
-UFUNCTION(BlueprintCallable, Category = "Thirdweb")
-void PerformNFTClaim();
-```
-
-**Build your project**
-
-Once it's done compiling, return UnrealEditor. In the `Outliner`, open the folder for `Collectibles` and click `Edit Collectible`. In the new window, click `File->Save As...` and save a copy as `CollectibleNFT`.
-
-Open the `Content Drawer` at the bottom, search for `CollectibleNFT`, and drag one into the scene. Find it in the `Outliner` and click `Edit Collectible NFT`.
-
-Find the `Perform Claim` function call and replace it with `Perform NFT Claim`. **Note** that the `Target` is passed from `Get Actor of Class`.
-
-
-
-
-
-You'll want to be able to tell this collectible apart, so click on the mesh for `Collectible` on the left side in the `Component` tree, then on the `Details` panel on the right, find the `Materials` section and change it to `MI_Solid_Blue`.
-
-Click the icons at the top to `Compile` and save your asset.
-
-From the content drawer, drag your asset into the viewport.
-
-You should now see a blue orb floating where you placed it.
-
-Make sure the orb is low enough to drive through, then run the game. Collect the orb, then verify on a block explorer that you received the NFT.
-
-### Tinting the Car
-
-In the content browser, open `All>Content>Vehicles>SportsCar>Materials`. Right-click in an empty spot and select `Material>Material Parameter Collection`. Name yours `NFT_MPS`. Open the collection, click the `+` to add an item to `Vector Parameters` and create the color of your choosing. Bright red is a good option to make your change very visible.
-
-Right-click in an empty spot again and select `Create Basic Asset>Material`. Name your new material `M_NFT_Color`. Open it by double-clicking.
-
-Right-click on the graph and add a `Collection Parameter` node. In the `Details` panel on the left, select your `NFT_MPS` collection and pick the first vector for `Parameter Name`
-
-Connect the output to the `Base Color` of `M_NFT_Color`, then save and close the editor.
-
-Again in the content browser, right-click on the `M_NFT_Color` asset and select `Create Material Instance`. Name the instance `MI_NFT_Color`.
-
-Navigate to the sports car mesh located in `VehicleTemplate>Blueprints>SportsCar` and double-click to open the `SportsCar_pawn`. Select the `Mesh` from the `Components` tree and you should see the car in the editor.
-
-On the right side, change the `Element 2` material to `MI_NFT_Color`. The car is now bright red. Radical! Take your newly red car for a spin.
-
-### Fetching the NFT Colors
-
-Return to `engine-express` and open `engineController.ts`. Add a function to `getNFTColors` that uses the `read` endpoint to call the `getNFTsOwned` function.
-
-```tsx
-export const getNFTColors = async (req: Request, res: Response) => {
- const { authToken } = req.body;
- if (!authToken || !userTokens[authToken]) {
- return res.status(400).json({ message: 'Invalid auth token' });
- }
- const user = userTokens[authToken];
- try {
- const url = `${ENGINE_URL}/contract/${CHAIN}/${RANDOM_COLOR_NFT_CONTRACT}/read?functionName=getNftsOwned&args=${user.ethAddress}`;
- const headers = {
- 'x-backend-wallet-address': BACKEND_WALLET,
- Authorization: `Bearer ${process.env.THIRDWEB_API_SECRET_KEY}`,
- };
-
- const response = await axiosInstance.get(url, { headers: headers });
-
- // TODO: Extract the color from the image
-
- // TODO: Replace response
- res.json(response.data);
- } catch (error) {
- console.error(error);
- res.status(400).json({ message: 'Error getting NFT data' });
- }
-};
-```
-
-You'll also need to add this function to `engineRoutes.ts`:
-
-```tsx
-router.post('/get-nft-colors', getNFTColors);
-```
-
-Return to `engineController.ts`.
-
-Because Unreal doesn't support SVGs, you'll need to extract the color from your NFT metadata, and pass that to use in the material you created. Start by adding a type for the response, and for the JSON metadata:
-
-```tsx
-type NFTData = {
- tokenId: bigint;
- metadata: string;
-};
-
-type JSONMetadata = {
- name: string;
- description: string;
- image: string;
-};
-```
-
-You'll also need helper functions to decode the base64 encoded metadata and SVG, then get the color from the SVG.
-
-```tsx
-function getJsonMetadata(nft: NFTData) {
- const base64String = nft.metadata.split(',')[1];
- const jsonString = atob(base64String);
- return JSON.parse(jsonString) as JSONMetadata;
-}
-
-function getColorFromBase64StringSVG(base64String: string) {
- const base64Data = base64String.split(',')[1];
- const svgString = atob(base64Data);
- const color = svgString.match(/fill=['"](#[0-9a-fA-F]{6})['"]/);
- return color ? color[1] : '#000000';
-}
-```
-
-Use these to extract an array of colors and return it:
-
-```tsx
-const nfts = response.data.result.map((item: any) => {
- return {
- tokenId: item[0],
- metadata: item[1],
- };
-});
-
-const metadata = nfts.map((nft: NFTData) => getJsonMetadata(nft));
-const colors = metadata.map((m: JSONMetadata) => getColorFromBase64StringSVG(m.image));
-
-res.json(colors);
-// Delete res.json(response.data);
-```
-
-
-
-To test with Postman or similar, comment out the check for a valid `authToken` and hardcode in an address that you know has NFTs.
-
-
-
-### Getting the Colors into the Game
-
-Return to the game in your code editor, and open `ThirdwebManager.cpp` and `ThirdwebManager.h`. As before, add a function to call and endpoint on your server. This time to retrieve the array of colors. You'll need to do a little more for this one to set an in-game variable for the colors.
-
-First, you'll need to add a new multicast delegate type to handle the response in `ThirdwebManager.h`:
-
-```c++
-// ThirdwebManager.h
-DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNFTColorsResponse, bool, bWasSuccessful, const TArray &, ResponseArray);
-```
-
-And expose it to the editor:
-
-```c++
-// ThirdwebManager.h
-// This delegate is triggered in C++, and Blueprints can bind to it.
-UPROPERTY(BlueprintAssignable, Category = "Thirdweb", meta = (DisplayName = "OnNFTColorsResponse"))
-FOnNFTColorsResponse;;
-```
-
-Then, add the function to `ThirdwebManager.cpp`. It's similar, but instead hits the endpoint for the NFT color array and uses the response you just created. It also expects the response to be an array of strings instead of searching for a property called `result`:
-
-```c++
-// ThirdwebManager.cpp
-void AThirdwebManager::GetNFTColors()
-{
- TSharedRef HttpRequest = FHttpModule::Get().CreateRequest();
- HttpRequest->SetURL(this->ServerUrl + "/engine/get-nft-colors"); // The endpoint to get the NFT colors
- HttpRequest->SetVerb("POST");
- HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
-
- TSharedPtr JsonObject = MakeShareable(new FJsonObject);
- JsonObject->SetStringField("authToken", AuthToken);
-
- FString OutputString;
- TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString);
- FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
-
- UE_LOG(LogTemp, Warning, TEXT("OutputString: %s"), *OutputString);
-
- HttpRequest->SetContentAsString(OutputString);
-
- HttpRequest->OnProcessRequestComplete().BindLambda([this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
- {
- if (bWasSuccessful && Response.IsValid())
- {
- int32 StatusCode = Response->GetResponseCode();
- if (StatusCode == 200)
- {
- TArray> JsonArray;
- TSharedRef> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
- if (FJsonSerializer::Deserialize(Reader, JsonArray) && JsonArray.Num() > 0)
- {
- TArray ResponseArray;
- for (const TSharedPtr& Value : JsonArray)
- {
- FString StringValue;
- if (Value->TryGetString(StringValue))
- {
- ResponseArray.Add(StringValue);
- }
- }
- this->OnNFTColorsResponse.Broadcast(true, ResponseArray);
- UE_LOG(LogTemp, Warning, TEXT("Get NFT Color response: %s"), *Response->GetContentAsString());
- return;
- }
- this->OnNFTColorsResponse.Broadcast(false, TArray());
- }
- else
- {
- FString ErrorMsg = FString::Printf(TEXT("HTTP Error: %d, Response: %s"), StatusCode, *(Response->GetContentAsString()));
- TArray ErrorArray;
- ErrorArray.Add(ErrorMsg);
- this->OnNFTColorsResponse.Broadcast(false, ErrorArray);
- UE_LOG(LogTemp, Warning, TEXT("ErrorMsg: %s"), *ErrorMsg);
- }
- }
- else
- {
- TArray ErrorArray;
- ErrorArray.Add(TEXT("Failed to connect to the server."));
- this->OnNFTColorsResponse.Broadcast(false, ErrorArray);
- UE_LOG(LogTemp, Warning, TEXT("Failed to connect to the server."));
- } });
-
- HttpRequest->ProcessRequest();
-}
-```
-
-Finally, expose this function to the editor.
-
-```c++
-// ThirdwebManager.h
-// Function to perform the get NFT Colors operation
-UFUNCTION(BlueprintCallable, Category = "Thirdweb")
-void GetNFTColors();
-```
-
-Compile and reload the project in the editor.
-
-In the content browser, find and open `Content>_Thirdweb>Blueprints>Canvas_HUD`.
-
-Under the text field for `Tokens`, drag a new `Text` widget in. Set the name at the top to `Label_Colors` and check `Is Variable`. Change the `Content` to `Colors`. If you put it on the right side, move the `Anchor` to the upper right corner.
-
-In the upper right, click the `Graph` tab. Add a `Sequence` node to split the flow after `Get Actor Of Class`. Following the same pattern as the flow that gets the balance response, add one that gets the NFT colors.
-
-Create the `Bind Event to OnNFTColorsResponse` node first, then create the `Custom Event` node from dragging from `Event`.
-
-For now, simply grab the last color in the array and set it in the HUD. To get it, drag off the `Response Array` in `OnNFTColorResponseReceived` and add a `Last Index` node. Drag again from the `Response Array` and add a `Get (Ref)` node. Connect the output of `Last Index` to the `Input` of `Get`. From there, drag from the output of `Get` and add a `To Text (String)` node.
-
-Drag out of the exec (white) connector of `OnNFTColorResponseReceived` and add a `Branch` and connect `Was Successful` to the `Condition`. For the `True` state, drag and add a `SetText (Text)`. Right click and add a reference to `Label Colors` and drag it to the `Target` of `SetText`. Connect the `Return Value` of `To Text (String)` to `In Text`.
-
-Finally, drag off `Bind Event to OnNFTColorsResponse` and add a `Set Timer by Function Name` node. Connect the `Return Value` of `Get Actor Of Class` to `Object`. Set the `Function Name` to `GetNFTColors` and the `Time` to `2.0`.
-
-You should end up with something like this:
-
-
-
-
-
-Compile the blueprint then run the game. You should see that last color in the array in the HUD, and you should see the full list printed in the console every two seconds.
-
-
-
-If you have an error in your `GetNFTColors` function that prevents `.Broadcast` from being called, nothing in the NFT Colors branch of this blueprint will run, including printing to the console.
-
-
-
-### Changing the Color of the Car
-
-Now that you have the colors, you can use them to change the color of your car! For now, you can just set the car to the last color, but on your own you'll want to add a UI widget to allow the player to pick their color.
-
-
-
-If you really wanted to get fancy, you could modify the contract to emit an `event` containing the color of the newly-minted NFT, extract that from the receipt, and optimistically make it available to the player a few seconds earlier.
-
-
-
-Unreal doesn't use hex colors, so you'll need to convert your hex string to a linear color and save it in the `Material Parameter Collection` you created earlier.
-
-Converting the hex code with a blueprint is very complicated. Luckily, Unreal has a helpful community that has created many utilities, including a [conversion function].
-
-
-
-Copying and pasting code for a game engine isn't quite as dangerous as copying and pasting unknown smart contract code, but you're working at the intersection of these worlds. Be sure to review and make sure you understand anything you find online.
-
-
-
-In the content browser, add a `Blueprints>Blueprint Function Library` called `ColorUtils`. In it, add a function called `HexStringToColor`.
-
-Copy the code from the community site and paste it into the function. Connect the `Hex String to Color` node to the `SET` node attached to `Make Array`, then from `SET` to the `Return Node`.
-
-Compile, and you'll get an error. Find and right-click on the error in the `Hex Code` node, then select `Create local variable`. Recompile and the error will resolve.
-
-You also need to input the string you want converted. Select the `Hex String to Color` node and click the `+` button by `Inputs` located in the panel on the right. Name it `hexString` and give it a `string` type. `Hex String` will now appear as a value in the `Hex String to Color` node. Connect it to the `Source String` input in the `Replace` node.
-
-Compile one last time, then save and close `ColorUtils`.
-
-Return to `Canvas_HUD` and open the `Graph`. Drag out of the `SetText` node that adds the color to the HUD and add a `Hex String to Color` node. The function expects alpha values in the hex code. To add this connect a second output of the string array `GET` to an `Append` function and append `ff` in the `B` input. Connect the `Return Value` to the `Hex String` input in `Hex String to Color`.
-
-Finally, add a `Set Vector Parameter Value`. Select `NFT_MPS` for the collection and `Vector` for the `Parameter Name`. Connect the `Liner Color` output of `Hex String to Color` to the `Parameter Value` input.
-
-
-
-
-
-Compile, save, and close `Canvas_HUD`. Run the game. Your car will start red, but after the response from the server, it will turn the color of your last NFT! Drive and collect the NFT collectible, and it will change colors!
-
-## Conclusion
-
-In this tutorial, you've learned how to set up Thirdweb's engine and use it to connect an Unreal Engine game to Base. You've also learned how to use their platform to deploy and manager your contracts. Finally, you've learned how to build game elements to allow players to collect new NFTs and use them to personalize their game items.
-
-[Base Learn]: https://docs.base.org/learn/welcome
-[ERC-721 Tokens]: https://docs.base.org/learn/erc-721-token/erc-721-standard-video
-[OpenZeppelin ERC-721]: https://docs.openzeppelin.com/contracts/2.x/api/token/erc721
-[OpenZeppelin]: https://www.openzeppelin.com/
-[Unreal Engine]: https://www.unrealengine.com/en-US
-[thirdweb]: https://thirdweb.com/
-[Gaming SDK]: https://portal.thirdweb.com/solutions/gaming/overview
-[Unreal Engine Quickstart]: https://portal.thirdweb.com/solutions/gaming/unreal-engine/quickstart
-[contract provided below]: #random-color-nft-contract
-[Engine]: https://github.com/thirdweb-dev/engine
-[run it locally]: https://portal.thirdweb.com/engine/self-host
-[Docker]: https://www.docker.com/
-[Postgres]: https://www.postgresql.org/
-[thirdweb API key]: https://thirdweb.com/dashboard/settings/api-keys
-[environment variables]: https://portal.thirdweb.com/engine/self-host#environment-variables
-[engine-express]: https://github.com/thirdweb-example/engine-express
-[Token Drop]: https://thirdweb.com/thirdweb.eth/DropERC20
-[Unreal Demo]: https://github.com/thirdweb-example/unreal_demo
-[thirdweb engine dashboard]: https://thirdweb.com/dashboard/engine
-[wallet best practices]: https://portal.thirdweb.com/engine/features/backend-wallets#best-practices
-[conversion function]: https://blueprintue.com/blueprint/vm4ujcqe/
-[Simple Onchain NFTs]: /tutorials/simple-onchain-nfts
-
diff --git a/docs/learn/welcome.mdx b/docs/learn/welcome.mdx
deleted file mode 100644
index c29f9d601..000000000
--- a/docs/learn/welcome.mdx
+++ /dev/null
@@ -1,27 +0,0 @@
----
-sidebarTitle: Welcome
-title: Learn to Build Smart Contracts and Onchain Apps
-description: Base Learn is a comprehensive, free guide to learning smart contract and onchain app development.
----
-
-import LearningObjectives from '/snippets/learning-objectives.mdx';
-
-
-
-
-
-## Introduction
-
-Welcome to Base Learn, your guide to learning smart contract development. Base Learn's curriculum has been expertly crafted to equip you with the skills and knowledge needed to build and deploy smart contracts on Base, or any EVM-compatible chain, including Ethereum, Optimism, and many more. Plus, you'll be eligible to earn NFTs as you complete each module, showcasing your mastery of the material.
-
-Whether you're a curious novice or a seasoned pro looking to stay ahead of the game, our dynamic lessons cater to all levels of experience. You can start with the basics and work your way up, or dive straight into the more advanced concepts and push your limits to new heights.
-
-Begin your journey today!
-
-## What you can learn in this program
-
-Base Learn covers the following topics. If you're looking for quickstarts, or deeper guides on advanced topics, check out our [Base Builder Tutorials](/)!
-
-
-
-