diff --git a/.github/linkChecker.ts b/.github/linkChecker.ts index f1dfca9f..16bfb4a6 100644 --- a/.github/linkChecker.ts +++ b/.github/linkChecker.ts @@ -3,7 +3,7 @@ import { readFileSync } from 'fs'; import { load } from 'cheerio'; import { sync as globSync } from 'glob'; -const baseUrl = 'https://academy.avax.network'; +const baseUrl = 'http://localhost:3000'; // base url of the website const whitelist = [] // some websites return 404 for head requests, so we need to whitelist them, (fix: pass header -H 'Accept: text/html' and parse text/html) // see https://github.com/rust-lang/crates.io/issues/788 @@ -34,7 +34,7 @@ function isValidURLOrPath(url: string): boolean { async function checkLink(url: string): Promise { try { const response = await get(url, { - timeout: 10000, // timeout to 10 seconds + timeout: 20000, // timeout to 20 seconds maxRedirects: 5, // handle up to 5 redirects validateStatus: function (status) { return status >= 200 && status < 400; // resolve only if the status code is less than 400 diff --git a/.github/workflows/checklinks.yml b/.github/workflows/checklinks.yml index 56a4a249..67d0125d 100644 --- a/.github/workflows/checklinks.yml +++ b/.github/workflows/checklinks.yml @@ -4,6 +4,7 @@ on: pull_request: paths: - '**/*.mdx' + branches: [dev] jobs: check-links: @@ -16,6 +17,14 @@ jobs: - name: Install dependencies run: | yarn install --frozen-lockfile - yarn global add tsx + yarn global add tsx wait-on + - name: Start Dev Server in Background + run: | + yarn dev & + - name: Wait for Dev Server to be Ready + run: | + wait-on http://localhost:3000 - name: Check links run: yarn check-links + - name: Stop Dev Server + run: kill $(jobs -p) || true diff --git a/content/course/avacloudsdk/02-glacier-overview/01-about-glacier.mdx b/content/course/avacloudsdk/02-glacier-overview/01-about-glacier.mdx new file mode 100644 index 00000000..4a36e463 --- /dev/null +++ b/content/course/avacloudsdk/02-glacier-overview/01-about-glacier.mdx @@ -0,0 +1,37 @@ +--- +title: Features +description: Learn about the features of the AvaCloud API. +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- + +## What is the Data API? + +The Data API provides web3 application developers with multi-chain data related to Avalanche’s primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata. + +### Data API Features +- **Extensive L1 Support**: Gain access to data from over 100+ L1s across both mainnet and testnet. If an L1 is listed on the [Avalanche Explorer](https://subnets.avax.network/stats/), you can query its data using the Data API. +- **Transactions and UTXOs**: Easily retrieve details related to transactions, UTXOs, and token transfers from Avalanche EVMs, Ethereum, and Avalanche’s Primary Network (P-Chain, X-Chain and C-Chain). +- **Blocks**: Retrieve latest blocks and block details +- **Balances**: Fetch balances of native, ERC-20, ERC-721, and ERC-1155 tokens along with relevant metadata. +- **Tokens**: Augment your user experience with asset details. +- **Staking**: Get staking related data for active and historical validations. + +## What is the Metrics API? +The Metrics API equips web3 developers with a robust suite of tools to access and analyze on-chain activity across Avalanche’s primary network, Avalanche L1s, and other supported EVM chains. This API delivers comprehensive metrics and analytics, enabling you to seamlessly integrate historical data on transactions, gas consumption, throughput, staking, and more into your applications. + +The Metrics API, along with the Data API are the driving force behind every graph you see on the [Avalanche Explorer](https://subnets.avax.network/stats/). From transaction trends to staking insights, the visualizations and data presented are all powered by these APIs, offering real-time and historical insights that are essential for building sophisticated, data-driven blockchain products. + +### Metrics API Features +- **Chain Throughput**: Retrieve detailed metrics on gas consumption, Transactions Per Second (TPS), and gas prices, including rolling windows of data for granular analysis. +- **Cumulative Metrics**: Access cumulative data on addresses, contracts, deployers, and transaction counts, providing insights into network growth over time. +- **Staking Information**: Obtain staking-related data, including the number of validators and delegators, along with their respective weights, across different subnets. +- **Blockchains and Subnets**: Get information about supported blockchains, including EVM Chain IDs, blockchain IDs, and subnet associations, facilitating multi-chain analytics. +- **Composite Queries**: Perform advanced queries by combining different metric types and conditions, enabling detailed and customizable data retrieval. + +## What is the AvaCloudSDK? + +The [AvaCloud SDK](https://developers.avacloud.io/avacloud-sdk/getting-started) provides web3 application developers with multi-chain data related to Avalanche’s primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata. + +The SDK is currently available in TypeScript, with more languages coming soon. If you are interested in a language that is not listed, please reach out to us in the [`#dev-tools`](https://discord.com/login?redirect_to=%2Flogin%3Fredirect_to%3D%252Fchannels%252F578992315641626624%252F1280920394236297257) channel in the [Avalanche Discord](https://discord.com/invite/avax). diff --git a/content/course/avacloudsdk/02-glacier-overview/02-glacier-vs-rpc.mdx b/content/course/avacloudsdk/02-glacier-overview/02-glacier-vs-rpc.mdx new file mode 100644 index 00000000..b033b93c --- /dev/null +++ b/content/course/avacloudsdk/02-glacier-overview/02-glacier-vs-rpc.mdx @@ -0,0 +1,66 @@ +--- +title: APIs vs RPCs +description: Learn how the AvaCloud Data API differs from RPC calls +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- + +Blockchain RPCs and APIs both facilitate interactions with a network, but they differ significantly in how they operate. + +### RPCs +**Blockchain RPCs** allow you to communicate directly with a blockchain node, performing tasks like querying data or submitting transactions. These are low-level, synchronous calls, requiring a deep understanding of the blockchain's structure and specific commands. + +To get a more comprehensive understanding of Ethereum's JSON-RPC API, you can refer to the [official Ethereum documentation](https://ethereum.org/en/developers/docs/apis/json-rpc/). + +### APIs +**Blockchain APIs**, like the AvaCloud Data API, abstract away much of the complexity. They offer higher-level, user-friendly endpoints that streamline interactions, making it easier to build and manage blockchain applications without needing in-depth knowledge of the underlying blockchain protocols. + +To get a more comprehensive understanding of the AvaCloud Data API, you can refer to the [official AvaCloud Data API documentation](https://developers.avacloud.io/data-api/overview). + +### Example Use Case +For example, querying a user's ERC-20 portfolio using an RPC involves a series of complex calls to retrieve and parse raw blockchain data. +Using just RPCs, you would need to: + +1. Query every block on the network for transaction logs. +2. Parse each transaction log to identify ERC-20 token transfers. +3. Extract the ERC-20 token contract address. +4. For each ERC-20 token contract, query the user's address to get the balance. +5. Parse and aggregate the data to present the user's portfolio. + +While it may seem simple in theory, this process can be time-consuming and error-prone, especially when dealing with multiple blockchains. + +With the AvaCloud Data API, you could simply use a dedicated endpoint such as: +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc20 \ + --header 'x-glacier-api-key: ' +``` + +to get a neatly formatted response with the user's ERC-20 portfolio, significantly reducing development time and complexity. + +```json +{ + "nextPageToken": "", + "erc20TokenBalances": [ + { + "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", + "name": "Wrapped AVAX", + "symbol": "WAVAX", + "decimals": 18, + "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg", + "ercType": "ERC-20", + "price": { + "currencyCode": "usd", + "value": "42.42" + }, + "chainId": "43114", + "balance": "2000000000000000000", + "balanceValue": { + "currencyCode": "usd", + "value": "42.42" + } + } + ] +} +``` \ No newline at end of file diff --git a/content/course/avacloudsdk/02-glacier-overview/03-glacier-endpoints.mdx b/content/course/avacloudsdk/02-glacier-overview/03-glacier-endpoints.mdx new file mode 100644 index 00000000..691ccc37 --- /dev/null +++ b/content/course/avacloudsdk/02-glacier-overview/03-glacier-endpoints.mdx @@ -0,0 +1,370 @@ +--- +title: Data API +description: Learn about AvaCloud Data API. +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- +![](/course-images/avacloudsdk/glacier-data-api.png) + +The AvaCloud Data API provides a comprehensive set of endpoints to interact with Avalanche networks. These endpoints allow you to query information about blocks, transactions, assets, and more. + +Below are some of the key endpoints available in the Data API. +A more comphrensive list of Data API endpoints can be found [here](https://developers.avacloud.io/data-api/overview). + +## Data API Reference +### EVM Endpoints + + + +- [List All Chains](https://developers.avacloud.io/data-api/evm-chains/list-chains) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains \ + --header 'accept: application/json' +``` +- [Get Chain Information](https://developers.avacloud.io/data-api/evm-chains/get-chain-information) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId} \ + --header 'accept: application/json' +``` + + + +- [List Latest Blocks](https://developers.avacloud.io/data-api/evm-chains/list-latest-blocks) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/blocks \ + --header 'accept: application/json' +``` +- [Get Block Information](https://developers.avacloud.io/data-api/evm-chains/getblock) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/blocks/{blockId} \ + --header 'accept: application/json' +``` + + + + +- [Get Deployment Transaction](https://developers.avacloud.io/data-api/evm-transactions/get-deployment-transaction) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/contracts/{address}/transactions:getDeployment \ + --header 'accept: application/json' +``` +- [List Deployed Contracts](https://developers.avacloud.io/data-api/evm-transactions/list-deployed-contracts) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/contracts/{address}/deployments \ + --header 'accept: application/json' +``` +- [List ERC Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-transfers) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/tokens/{address}/transfers \ + --header 'accept: application/json' +``` + +- [List Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-transactions) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions \ + --header 'accept: application/json' +``` + +- [List Native Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-native-transactions) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listNative \ + --header 'accept: application/json' +``` + +- [List ERC-20 Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-20-transfers) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listErc20 \ + --header 'accept: application/json' +``` + +- [List ERC-721 Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-721-transfers) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listErc721 \ + --header 'accept: application/json' +``` + +- [List ERC-1155 Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-1155-transfers) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listErc1155 \ + --header 'accept: application/json' +``` +- [List Internal Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-internal-transactions) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listInternals \ + --header 'accept: application/json' +``` + +- [Get Transaction](https://developers.avacloud.io/data-api/evm-transactions/get-transaction) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/transactions/{txHash} \ + --header 'accept: application/json' +``` + +- [List Transactions For a Block](https://developers.avacloud.io/data-api/evm-transactions/list-transactions-for-a-block) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/blocks/{blockId}/transactions \ + --header 'accept: application/json' +``` + +- [List Latest Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-latest-transactions) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/transactions \ + --header 'accept: application/json' +``` + + + +- [Get Native Token Balance](https://developers.avacloud.io/data-api/evm-balances/get-native-token-balance) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:getNative \ + --header 'accept: application/json' +``` +- [List ERC-20 Balances](https://developers.avacloud.io/data-api/evm-balances/list-erc-20-balances) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc20 \ + --header 'accept: application/json' +``` +- [List ERC-721 Balances](https://developers.avacloud.io/data-api/evm-balances/list-erc-721-balances) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc721 \ + --header 'accept: application/json' +``` +- [List ERC-1155 Balances](https://developers.avacloud.io/data-api/evm-balances/list-erc-1155-balances) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc1155 \ + --header 'accept: application/json' +``` +- [List Collectible (ERC-721 and ERC-1155) Balances](https://developers.avacloud.io/data-api/evm-balances/list-collectible-erc-721erc-1155-balances) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listCollectibles \ + --header 'accept: application/json' +``` + + + +- [Get Contract Metadata](https://developers.avacloud.io/data-api/evm-contracts/get-contract-metadata) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address} \ + --header 'accept: application/json' +``` + + + +- [Reindex NFT Metadata](https://developers.avacloud.io/data-api/nfts/reindex-nft-metadata) +```bash +curl --request POST \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/nfts/collections/{address}/tokens/tokenId:reindex \ + --header 'accept: application/json' +``` +- [List Tokens](https://developers.avacloud.io/data-api/nfts/list-tokens) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/nfts/collections/{address}/tokens \ + --header 'accept: application/json' +``` +- [Get Token Details](https://developers.avacloud.io/data-api/nfts/get-token-details) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/chains/{chainId}/nfts/collections/{address}/tokens/tokenId \ + --header 'accept: application/json' +``` + + + + + + + +### Avalanche Primary Network Endpoints + + + +- [Get Chain Interactions for Addresses](https://developers.avacloud.io/data-api/primary-network/get-chain-interactions-for-addresses) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/{network}/addresses:listChainIds \ + --header 'accept: application/json' +``` + +- [Get Network Details](https://developers.avacloud.io/data-api/primary-network/get-network-details) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/{network} \ + --header 'accept: application/json' +``` +- [List Blockchains](https://developers.avacloud.io/data-api/primary-network/list-blockchains) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/{network}/blockchains \ + --header 'accept: application/json' +``` + +- [List Subnets](https://developers.avacloud.io/data-api/primary-network/list-subnets) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/subnets \ + --header 'accept: application/json' +``` +- [Get Subnet Details by ID](https://developers.avacloud.io/data-api/primary-network/get-subnet-details-by-id) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/subnets/{subnetId} \ + --header 'accept: application/json' +``` + + + +- [List Validators](https://developers.avacloud.io/data-api/primary-network/list-validators) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/validators \ + --header 'accept: application/json' +``` +- [Get Single Validator Details](https://developers.avacloud.io/data-api/primary-network/get-single-validator-details) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/validators/{nodeId} \ + --header 'accept: application/json' +``` +- [List Delegators](https://developers.avacloud.io/data-api/primary-network/list-delegators) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/delegators \ + --header 'accept: application/json' +``` + + + +- [Get Block](https://developers.avacloud.io/data-api/primary-network-blocks/get-block) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/blocks/{blockId} \ + --header 'accept: application/json' +``` +- [List Blocks Proposed by Node](https://developers.avacloud.io/data-api/primary-network-blocks/list-blocks-proposed-by-node) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/nodes/{nodeId}/blocks \ + --header 'accept: application/json' +``` + +- [List Latest Blocks](https://developers.avacloud.io/data-api/primary-network-blocks/list-latest-blocks) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/blocks \ + --header 'accept: application/json' +``` + + + +- [List Vertices](https://developers.avacloud.io/data-api/primary-network-vertices/list-vertices) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/vertices \ + --header 'accept: application/json' +``` +- [Get Vertex](https://developers.avacloud.io/data-api/primary-network-vertices/get-vertex) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/vertices/vertexHash \ + --header 'accept: application/json' +``` +- [List Vertices by Height](https://developers.avacloud.io/data-api/primary-network-vertices/list-vertices-by-height) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/vertices:listByHeight \ + --header 'accept: application/json' +``` + + + +- [Get Transaction](https://developers.avacloud.io/data-api/primary-network-transactions/get-transaction) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/{network}/blockchains/{blockchainId}/transactions/{txHash} \ + --header 'accept: application/json' +``` +- [List Latest Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-latest-transactions) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/{network}/blockchains/{blockchainId}/transactions \ + --header 'accept: application/json' +``` +- [List Staking Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-staking-transactions) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/transactions:listStaking \ + --header 'accept: application/json' +``` + +- [List Asset Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-asset-transactions) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/assets/{assetId}/transactions \ + --header 'accept: application/json' +``` + + + + +- [List UTXOs](https://developers.avacloud.io/data-api/primary-network-utxos/list-utxos) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/utxos \ + --header 'accept: application/json' +``` +- [Get Balances](https://developers.avacloud.io/data-api/primary-network-balances/get-balances) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/balances \ + --header 'accept: application/json' +``` + + + +- [List Pending Rewards](https://developers.avacloud.io/data-api/primary-network-rewards/list-pending-rewards) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/rewards:listPending \ + --header 'accept: application/json' +``` +- [List Historical Rewards](https://developers.avacloud.io/data-api/primary-network-rewards/list-historical-rewards) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/rewards \ + --header 'accept: application/json' +``` + + + +- [Get Asset Details](https://developers.avacloud.io/data-api/primary-network/get-asset-details) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/assets/{assetId} \ + --header 'accept: application/json' +``` + + diff --git a/content/course/avacloudsdk/02-glacier-overview/04-glacier-webhooks.mdx b/content/course/avacloudsdk/02-glacier-overview/04-glacier-webhooks.mdx new file mode 100644 index 00000000..e9b697c8 --- /dev/null +++ b/content/course/avacloudsdk/02-glacier-overview/04-glacier-webhooks.mdx @@ -0,0 +1,140 @@ +--- +title: Webhooks API +description: Learn about AvaCloud Webhooks API. +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- +## What is a Webhook? + +A webhook is a communication mechanism to provide applications with real-time information. It delivers data to other applications as it happens, meaning you get data immediately, unlike typical APIs where you would need to poll for data to get it in "real-time". This makes webhooks much more efficient for both providers and consumers. Webhooks work by registering a URL to send notifications once certain events occur. +You can create receiver endpoints in your server in any programming language and those will have an associated URL. This object contains all the relevant information about what just happened, including the type of event and the data associated with that event. + +## What are AvaCloud Webhooks? + +With the Webhooks API, you can monitor real-time events on the Avalanche C-Chain and L1s. For example, you can monitor smart contract events, track NFT transfers, and observe wallet-to-wallet transactions. + +![](/course-images/avacloudsdk/glacier-webhooks.png) + +### Key Features: +- **Real-time notifications:** Receive immediate updates on specified on-chain activities without polling. +- **Customizable:** Specify the desired event type to listen for, customizing notifications according to individual requirements. +- **Secure:** Employ shared secrets and signature-based verification to guarantee that notifications originate from a trusted source. +- **Broad Coverage:** Support for C-chain mainnet, testnet, and subnets within the Avalanche ecosystem, ensuring wide-ranging monitoring capabilities. + +### Use Cases: +- **NFT Marketplace Transactions:** Get alerts for NFT minting, transfers, auctions, bids, sales, and other interactions within NFT marketplaces. +- **Wallet Notifications:** Receive alerts when an address performs actions such as sending, receiving, swapping, or burning assets. +- **DeFi Activities:** Receive notifications for various DeFi activities such as liquidity provisioning, yield farming, borrowing, lending, and liquidations. + +## Webhooks API Reference + + + + +[Create a new webhook](https://developers.avacloud.io/webhooks-api/webhooks/create-a-webhook) +```bash +curl --request POST \ + --url https://glacier-api.avax.network/v1/webhooks \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + --data ' +{ + "eventType": "address_activity" +} +``` + + + +[Lists webhooks for the user.](https://developers.avacloud.io/webhooks-api/webhooks/list-webhooks) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/webhooks \ + --header 'accept: application/json' +``` + + + + +[Retrieves a webhook by ID.](https://developers.avacloud.io/webhooks-api/webhooks/get-a-webhook-by-id) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/webhooks/id \ + --header 'accept: application/json' +``` + + + + +[Deactivates a webhook by ID.](https://developers.avacloud.io/webhooks-api/webhooks/deactivate-a-webhook) +```bash +curl --request DELETE \ + --url https://glacier-api.avax.network/v1/webhooks/id \ + --header 'accept: application/json' +``` + + + + +[Updates an existing webhook.](https://developers.avacloud.io/webhooks-api/webhooks/update-a-webhook) +```bash +curl --request PATCH \ + --url https://glacier-api.avax.network/v1/webhooks/id \ + --header 'accept: application/json' \ + --header 'content-type: application/json' +``` + + + +[Generates a new shared secret.](https://developers.avacloud.io/webhooks-api/webhooks/generate-a-shared-secret) +```bash +curl --request POST \ + --url https://glacier-api.avax.network/v1/webhooks:generateOrRotateSharedSecret \ + --header 'accept: application/json' +``` + + + + +[Get a previously generated shared secret.](https://developers.avacloud.io/webhooks-api/webhooks/get-a-shared-secret) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/webhooks:getSharedSecret \ + --header 'accept: application/json' +``` + + + + +[Add addresses to webhook.](https://developers.avacloud.io/webhooks-api/webhooks/add-addresses-to-webhook) +```bash +curl --request PATCH \ + --url https://glacier-api.avax.network/v1/webhooks/id/addresses \ + --header 'accept: application/json' \ + --header 'content-type: application/json' +``` + + + +[Remove addresses from webhook.](https://developers.avacloud.io/webhooks-api/webhooks/remove-addresses-from-webhook) +```bash +curl --request DELETE \ + --url https://glacier-api.avax.network/v1/webhooks/id/addresses \ + --header 'accept: application/json' \ + --header 'content-type: application/json' +``` + + + + +[List adresses by webhook.](https://developers.avacloud.io/webhooks-api/webhooks/list-adresses-by-webhook) +```bash +curl --request GET \ + --url https://glacier-api.avax.network/v1/webhooks/id/addresses \ + --header 'accept: application/json' +``` + + + + + \ No newline at end of file diff --git a/content/course/avacloudsdk/03-environment-setup/01-avacloud-account.mdx b/content/course/avacloudsdk/03-environment-setup/01-avacloud-account.mdx new file mode 100644 index 00000000..6f033b9f --- /dev/null +++ b/content/course/avacloudsdk/03-environment-setup/01-avacloud-account.mdx @@ -0,0 +1,48 @@ +--- +title: Create API Key +description: Get an API key for AvaCloud +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- +import { Step, Steps } from 'fumadocs-ui/components/steps'; + + +## Get an AvaCloud API Key + +In order to utilize your accounts rate limits, you will need to make API requests with an API key. You can generate API Keys from the [AvaCloud portal](https://app.avacloud.io/glacier-api/). + + + + +Create an account on [AvaCloud](https://avacloud.io). + + + +Click on the `Web3 Data API` [tab](https://app.avacloud.io/glacier-api/). +![](/course-images/avacloudsdk/create-api-key.png) + + + +Click `+ Add API Key` and create a new name for your API key. +![](/course-images/avacloudsdk/name-api-key.png) + + + +**Save your API key** in a secure location. We will need it in the next step. +![](/course-images/avacloudsdk/grab-api-key.png) + + + + +After generating the API keys the AvaCloud page should look like this: +![](/course-images/avacloudsdk/final-api-key.png) + +Once you've created and retrieved the key, you will be able to make authenticated queries by passing in your API key in the `x-glacier-api-key` header of your HTTP request. + +An example curl request to the Data API: + +```bash +curl -H "Content-Type: Application/json" -H "x-glacier-api-key: " \ + "https://glacier-api.avax.network/v1/chains" +``` \ No newline at end of file diff --git a/content/course/avacloudsdk/03-environment-setup/02-setup-starter-kit.mdx b/content/course/avacloudsdk/03-environment-setup/02-setup-starter-kit.mdx new file mode 100644 index 00000000..52167aa9 --- /dev/null +++ b/content/course/avacloudsdk/03-environment-setup/02-setup-starter-kit.mdx @@ -0,0 +1,86 @@ +--- +title: Setup AvaCloudSDK Starter Kit +description: Set up the AvaCloudSDK Starter Kit in a GitHub Codespace +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- +import Link from 'next/link'; +import { cn } from '@/utils/cn'; +import { buttonVariants } from '@/components/ui/button.tsx' +import { Step, Steps } from 'fumadocs-ui/components/steps'; + +In this course we will run the AvaCloudSDK Starter Kit in a Github Codepsace. This is the quickest way to get started. + +The `AvaCloudSDK Starter Kit` contains everything we need to get started quickly with the AvaCloud API. + + + + +### Open the AvaCloudSDK Starter Kit Github Repository: +**Make sure you are on the `follow-along` branch!** + +Open AvaCloudSDK Starter Kit + + + + +### Create a Codespace +- Click the green **Code** button +- Switch to the **Codespaces** tab +- Click **Create Codespace on main** or if you already have a Codespace click the plus (**+**) button + +The Codespace will open in a new tab. Wait a few minutes until it's fully built. + + + + +### Verify everything is working correctly + +Open the terminal with `` Ctrl + ` `` or by opening it through the menu: + +![](/course-images/avacloudsdk/terminal.png) + + + + +### Set the `AVACLOUD_API_KEY` environment variable +Create a `.env` file in the root of the project and add your API key from [the previous step](/course/avacloudsdk/03-environment-setup/01-avacloud-account): + +```bash +AVACLOUD_API_KEY=ac_rGIKESl9_9DWuLfJJQLSV5nlzbKR7eHxym6XW3XEQJeNBDRxI... +``` + + + +### Start the NextJS app +Now we can run the NextJS app in hot reload mode: + +```bash +yarn dev +``` + +It should preview within the Codespace: +![](/course-images/avacloudsdk/site-running.png) + + + + + + +### Optional: Open Codespace locally in Visual Studio Code + +You can switch any time from the browser IDE to Visual Studio Code: + +![](/course-images/avacloudsdk/vs-code.png) + +The first time you switch, you will be asked to install the [Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces) and connect VS Code to you GitHub account, if it is not already connceted. + + + \ No newline at end of file diff --git a/content/course/avacloudsdk/03-environment-setup/03-what-we-build.mdx b/content/course/avacloudsdk/03-environment-setup/03-what-we-build.mdx new file mode 100644 index 00000000..24fa5bd0 --- /dev/null +++ b/content/course/avacloudsdk/03-environment-setup/03-what-we-build.mdx @@ -0,0 +1,20 @@ +--- +title: Time to Build! +description: Learn about what we will build in this course +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- + +### Finished Setup +Once your environment is set up, we can start building some applications using the AvaCloud API and the AvaCloudSDK. The home page should look like this: +![](/course-images/avacloudsdk/preview.png) + +### What We Will Build +Here is a brief overview of what we will be building in this course: + +- ERC-20 Balance App +- Wallet Portfolio App +- Block Explorer App + +Each of these applications will utilize the Data API to get data from the Avalanche network. We will fetch data such as ERC-20 token balances, NFT balances, recent transactions from an address, block information and much more. \ No newline at end of file diff --git a/content/course/avacloudsdk/04-erc20-token-balance-app/01-overview.mdx b/content/course/avacloudsdk/04-erc20-token-balance-app/01-overview.mdx new file mode 100644 index 00000000..0657b7a2 --- /dev/null +++ b/content/course/avacloudsdk/04-erc20-token-balance-app/01-overview.mdx @@ -0,0 +1,15 @@ +--- +title: Overview +description: Use the AvaCloud Data API to create a simple web app that displays a user's ERC-20 token balances. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- + +In this section we will use the Data API to create a simple web app that displays a user's ERC-20 token portfolio. This app will allow users to input their Avalanche C-Chain address and view a list of their ERC-20 token balances. + +![](/course-images/avacloudsdk/balance-app.png) +We will use two endpoints from the Data API to accomplish this: + +- [`data.evm.blocks.getLatestBlocks`](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) +- [`data.evm.balances.listErc20Balances`](https://developers.avacloud.io/data-api/evm-balances/list-erc-20-balances) diff --git a/content/course/avacloudsdk/04-erc20-token-balance-app/02-understanding-code.mdx b/content/course/avacloudsdk/04-erc20-token-balance-app/02-understanding-code.mdx new file mode 100644 index 00000000..2245891e --- /dev/null +++ b/content/course/avacloudsdk/04-erc20-token-balance-app/02-understanding-code.mdx @@ -0,0 +1,115 @@ +--- +title: Understanding the Code +description: Before we start coding, let's take a look at the code we will be working with. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- +import { Step, Steps } from 'fumadocs-ui/components/steps'; + +There will be two main files that we will be working with in this section. + + + + +### `Page.tsx` +This is the code that will be rendered on the client side, as distinguished by `"use client";` at the top of the file. It contains the React components that will be displayed to the user and is responsible for making the API calls to our backend, which in turn calls the Data API. + +It is important to understand that when you `"use client"` in a NextJS project, it will be rendered on the client side. This means that the code will be executed in the user's browser and not on the server. This is important to keep in mind when working with sensitive data or when you want to keep your API keys secure. + +Besides this, we have two main functions that we will be working with in this file: + + + + +```tsx title="src/app/balance-app/page.tsx" +const handleSetAddress = async () => { + // + // TODO: Implement handleSetAddress + // +}; +``` +`handleSetAddress` is a simple function that will be called when the user clicks the "Set Address" button. It will ensure the address is valid then set the inputted address to the React State. Next, we call our next function `fetchERC20Balances` to get the user's balance. + + + + +```tsx title="src/app/balance-app/page.tsx" +const fetchERC20Balances = async (address: string) => { + // + // TODO: Implement fetchERC20Balances + // +}; +``` + +`fetchERC20Balances` is a function that will make a call to our backend to get the user's ERC-20 token balances. It will first get the current block height, then call the `listErc20Balances` method on our backend with the user's address and the block height. It will then return the balances as an array of `Erc20TokenBalance` objects. + + + + + + + +### `Route.ts` +This code will be executed on the server side, as distinguished by `"use server";` at the top of the file. It contains the code that will be executed on the server side and is responsible for making the API calls to the Data API. + +There are a few key components to understand in this file: + + + +```tsx title="src/app/api/balance/route.ts" +import { AvaCloudSDK } from "@avalabs/avacloud-sdk"; +const avaCloudSDK = new AvaCloudSDK({ + apiKey: process.env.AVACLOUD_API_KEY, + chainId: "43114", // Avalanche Mainnet + network: "mainnet", +}); +``` +Here we initialize the `AvaCloudSDK` with our AvaCloud API key and the chainId of `43114` for the Avalanche Mainnet. This will allow us to make calls to the Data API. + + + +```tsx title="src/app/api/balance/route.ts" +export async function GET(request: Request) { + const { searchParams } = new URL(request.url) + const method = searchParams.get('method') + try { + let result + switch (method) { + case 'getBlockHeight': + result = await getBlockHeight() + break + case 'listErc20Balances': + const address: string = searchParams.get('address')! + const blockNumber: string = searchParams.get('blockNumber')! + result = await listErc20Balances(address, blockNumber); + break + default: + return NextResponse.json({ error: 'Invalid method' }, { status: 400 }) + } + return NextResponse.json(result) + } catch (error) { + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }) + } +} +``` +Here we define the internal API methods for our backend. We have two methods that we will be working with in this section: `getBlockHeight` and `listErc20Balances`. We create both of these methods internally, then forward the request to the Data API. We then return the result to the client. + + +```tsx title="src/app/api/balance/route.ts" +async function getBlockHeight() { + // + // TODO: Implement getBlockHeight + // +} +async function listErc20Balances(address: string, blockNumber: string) { + // + // TODO: Implement listErc20Balances + // +} +``` +In the next section, we will implement the `listErc20Balances` and `getBlockHeight` function to call the Data API through the AvaCloudSDK. + + + + \ No newline at end of file diff --git a/content/course/avacloudsdk/04-erc20-token-balance-app/03-modifying-code.mdx b/content/course/avacloudsdk/04-erc20-token-balance-app/03-modifying-code.mdx new file mode 100644 index 00000000..e50e61b9 --- /dev/null +++ b/content/course/avacloudsdk/04-erc20-token-balance-app/03-modifying-code.mdx @@ -0,0 +1,108 @@ +--- +title: Modifying the Code +description: Lets modify the code to implement the Data API. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- +import { Step, Steps } from 'fumadocs-ui/components/steps'; + +In this section we will modify the code to implement the Data API. + +### Modify Backend `src/app/api/balance/route.ts` + + + +First we will implement the `getBlockHeight` function. The goal of this function is to fetch recent blocks from the Data API then to return the highest block in the first position. + + + +Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) to see how to fetch the latest blocks. + + +```tsx title="src/app/api/balance/route.ts" +async function getBlockHeight() { + const result = await avaCloudSDK.data.evm.blocks.getLatestBlocks({ + pageSize: 1, + }); + return result.result.blocks[0].blockNumber +} +``` + + + + +Next we will implement the `listErc20Balances` function. The goal of this function is to fetch the ERC-20 token balances for a given address at a specific block height. + + +Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-balances/list-erc-20-balances) to see how to fetch ERC-20 token balances. +Note the `Erc20TokenBalance` type that is imported, we should use this to combine paged results. + + +```tsx title="src/app/api/balance/route.ts" +async function listErc20Balances(address: string, blockNumber: string) { + const result = await avaCloudSDK.data.evm.balances.listErc20Balances({ + blockNumber: blockNumber, + pageSize: 10, + address: address, + }); + const balances: Erc20TokenBalance[] = []; + for await (const page of result) { + balances.push(...page.result.erc20TokenBalances); + } + return balances +} +``` + + + + +### Modify Frontend `src/app/balance-app/page.tsx` + + + + + +First we will implement the `fetchERC20Balances` function. The goal of this function is to make a call to our backend to get the user's ERC-20 token balances. + + +Make a call to our backend first for the most recent block height, then our `listErc20Balances` API. Finally return the results. + + +```tsx title="src/app/balance-app/page.tsx" +const fetchERC20Balances = async (address: string) => { + const blockResult = await fetch("api/balance?method=getBlockHeight"); + const blockNumber = await blockResult.json(); + const balanceResult = await fetch("api/balance?method=listErc20Balances&address=" + address + "&blockNumber=" + blockNumber); + const balances = await balanceResult.json(); + return balances as Erc20TokenBalance[]; +}; +``` + + + + +Next we will implement the `handleSetAddress` function. The goal of this function is to set the address in the state and fetch the ERC-20 token balances for that address using our `fetchERC20Balances` function. + + +First make sure the address is valid, then update the state for `Address` and `Balances` + + +```tsx title="src/app/balance-app/page.tsx" +const handleSetAddress = async () => { + const addressInput = document.getElementById("address") as HTMLInputElement; + const address = addressInput.value; + const addressPattern = /^0x[a-fA-F0-9]{40}$/; + + if (addressInput && addressPattern.test(address)) { + setAddress(address); + setBalances(await fetchERC20Balances(address)); + } +}; +``` + + + + + + diff --git a/content/course/avacloudsdk/04-erc20-token-balance-app/04-final.mdx b/content/course/avacloudsdk/04-erc20-token-balance-app/04-final.mdx new file mode 100644 index 00000000..4c225ddc --- /dev/null +++ b/content/course/avacloudsdk/04-erc20-token-balance-app/04-final.mdx @@ -0,0 +1,16 @@ +--- +title: Final Result +description: The final result of the ERC-20 token balance app. +updated: 2024-09-03 +authors: [owenwahlgren] +icon: Book +--- + +If we implemented the code correctly, we should have a working ERC-20 token balance app that displays the user's token balances. The app will look like the following: +![](/course-images/avacloudsdk/balance-app-init.png) + +After setting the address field, the app will display the user's ERC-20 token balances: + +![](/course-images/avacloudsdk/balance-app.png) + +Notice how the app also displays images for each token. This is done by fetching the token metadata from `listErc20Balances` and displaying the token's logo. \ No newline at end of file diff --git a/content/course/avacloudsdk/05-wallet-portfolio-app/01-overview.mdx b/content/course/avacloudsdk/05-wallet-portfolio-app/01-overview.mdx new file mode 100644 index 00000000..d65df818 --- /dev/null +++ b/content/course/avacloudsdk/05-wallet-portfolio-app/01-overview.mdx @@ -0,0 +1,19 @@ +--- +title: Overview +description: Use the Data API to create a simple web app that displays data and metrics on a connected wallet. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- + +In this section we will expand on the ERC-20 Balance App with NFTs (ERC-721) and ERC-1155 tokens. We will also add a connect wallet button to allow users to connect their own wallet and view their own token balances. + +Here is a preview of what we will build: +![](/course-images/avacloudsdk/nfts.png) +![](/course-images/avacloudsdk/tokens.png) + +We will use a few additional endpoints from the Data API to accomplish this: + +- [`data.evm.blocks.listErc721Balances`](https://developers.avacloud.io/data-api/evm-balances/list-erc-721-balances) +- [`data.evm.balances.listErc1155Balances`](https://developers.avacloud.io/data-api/evm-balances/list-erc-1155-balances) +- [`data.evm.transactions.listTransactions`](https://developers.avacloud.io/data-api/evm-transactions/list-transactions) \ No newline at end of file diff --git a/content/course/avacloudsdk/05-wallet-portfolio-app/02-understanding-code.mdx b/content/course/avacloudsdk/05-wallet-portfolio-app/02-understanding-code.mdx new file mode 100644 index 00000000..eb7a4211 --- /dev/null +++ b/content/course/avacloudsdk/05-wallet-portfolio-app/02-understanding-code.mdx @@ -0,0 +1,149 @@ +--- +title: Understanding the Code +description: Before we start coding, let's take a look at the code we will be working with. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- +import { Step, Steps } from 'fumadocs-ui/components/steps'; + +There will be two main files that we will be working with in this section. + + + + +### `Page.tsx` +This is the code that will be rendered on the client side, as distinguished by `"use client";` at the top of the file. It contains the React components that will be displayed to the user and is responsible for making the API calls to our backend, which in turn calls the Data API. + +It is important to understand that when you `"use client"` in a NextJS project, it will be rendered on the client side. This means that the code will be executed in the user's browser and not on the server. This is important to keep in mind when working with sensitive data or when you want to keep your API keys secure. + +Besides this, we have three main functions that we will be working with in this file: + + + +```tsx title="src/app/basic-wallet/page.tsx" +const fetchERC721Balances = async (address: string) => { + // + // TODO: Implement this! + // + return [] as Erc721TokenBalance[]; + } +``` +`fetchERC721Balances` is a function that will make a call to our backend to get the user's ERC-721 token balances. It will call the `listErc721Balances` method on our backend with the user's address. It will then return the balances as an array of `Erc721TokenBalance` objects. + + + +```tsx title="src/app/basic-wallet/page.tsx" +const fetchERC1155Balances = async (address: string) => { + // + // TODO: Implement this! + // + return [] as Erc1155TokenBalance[]; + } +``` + +`fetchERC1155Balances` is a function that will make a call to our backend to get the user's ERC-1155 token balances. It will call the `listErc1155Balances` method on our backend with the user's address. It will then return the balances as an array of `Erc1155TokenBalance` objects. + + + +```tsx title="src/app/basic-wallet/page.tsx" + const fetchRecentTransactions = async (address: string) => { + // + // TODO: Implement this! + // + return {} as TransactionDetails; + } +``` +`fetchRecentTransactions` is a function that will make a call to our backend to get the user's recent transactions for all tokens. It will call the `listRecentTransactions` method on our backend with the user's address. It will then return the balances as an object of type `TransactionDetails`. + + + + + + + +### `Route.ts` +This code will be executed on the server side, as distinguished by `"use server";` at the top of the file. It contains the code that will be executed on the server side and is responsible for making the API calls to the Data API. + +There are a few key components to understand in this file: + + + +```tsx title="src/app/api/wallet/route.ts" +import { AvaCloudSDK } from "@avalabs/avacloud-sdk"; +const avaCloudSDK = new AvaCloudSDK({ + apiKey: process.env.AVACLOUD_API_KEY, + chainId: "43114", // Avalanche Mainnet + network: "mainnet", +}); +``` +Here we initialize the `AvaCloudSDK` with our AvaCloud API key and the chainId of `43114` for the Avalanche Mainnet. This will allow us to make calls to the Data API. + + + +```tsx title="src/app/api/wallet/route.ts" +export async function GET(request: Request) { + const { searchParams } = new URL(request.url) + const method = searchParams.get('method') + let address + try { + let result + switch (method) { + case 'listERC721Balances': + address = searchParams.get('address')! + result = await listERC721Balances(address) + break + case 'listERC1155Balances': + address = searchParams.get('address')! + result = await listErc1155Balances(address) + break + case 'listRecentTransactions': + address = searchParams.get('address')! + result = await listRecentTransactions(address) + break + default: + return NextResponse.json({ error: 'Invalid method' }, { status: 400 }) + } + return NextResponse.json(result) + } catch (error) { + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }) + } +} +``` +Here we define the internal API methods for our backend. We have two methods that we will be working with in this section: `getBlockHeight`, `listERC721Balances`, `listErc1155Balances` and `listRecentTransactions`. We create all of these methods internally, then forward the request to the Data API. We then return the result to the client. + + +```tsx title="src/app/api/wallet/route.ts" +async function getBlockHeight() { + // + // TODO: Implement this! + // + return +} + +const listERC721Balances = async (address: string) => { + // + // TODO: Implement this! + // + return +} + +const listErc1155Balances = async (address: string) => { + // + // TODO: Implement this! + // + return +} + +const listRecentTransactions = async (address: string) => { + // + // TODO: Implement this! + // + return +} +``` +In the next section, we will implement these functions to call the Data API through the AvaCloudSDK. + + + + \ No newline at end of file diff --git a/content/course/avacloudsdk/05-wallet-portfolio-app/03-modifying-code.mdx b/content/course/avacloudsdk/05-wallet-portfolio-app/03-modifying-code.mdx new file mode 100644 index 00000000..2f7cb2f0 --- /dev/null +++ b/content/course/avacloudsdk/05-wallet-portfolio-app/03-modifying-code.mdx @@ -0,0 +1,238 @@ +--- +title: Modifying the Code +description: Lets modify the code to implement the Data API. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- +import { Step, Steps } from 'fumadocs-ui/components/steps'; + +In this section, we will modify the code to implement the Data API. + +### Modify Backend `src/app/api/wallet/route.ts` + + +First we will implement the `getBlockHeight` function. This function is the same as the one in ERC20 Balance App, but we will repeat it for the sake of the tutorial. The goal of this function is to fetch recent blocks from the Data API then to return the highest block in the first position. + + + +Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) to see how to fetch the latest blocks. + + +```tsx title="src/app/api/wallet/route.ts" +async function getBlockHeight() { + const result = await avaCloudSDK.data.evm.blocks.getLatestBlocks({ + pageSize: 1, + }); + return result.result.blocks[0].blockNumber +} +``` + + + + +Next we will implement the `listERC721Balances` function. The goal of this function is to fetch the ERC-721 token balances for a given address. + + +Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-balances/list-erc-721-balances) to see how to fetch ERC-721 token balances. +Note the `Erc721TokenBalance` type that is imported, we should use this to combine paged results. + + +```tsx title="src/app/api/wallet/route.ts" +const listERC721Balances = async (address: string) => { + const result = await avaCloudSDK.data.evm.balances.listErc721Balances({ + pageSize: 10, + address: address, + }); + const balances: Erc721TokenBalance[] = []; + for await (const page of result) { + balances.push(...page.result.erc721TokenBalances); + } + return balances +} +``` + + + + +Now we will implement the `listErc1155Balances` function. The goal of this function is to fetch the ERC-1155 token balances for a given address. + + +Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-balances/list-erc-1155-balances) to see how to fetch ERC-1155 token balances. +Note the `Erc1155TokenBalance` type that is imported, we should use this to combine paged results. + + +```tsx title="src/app/api/wallet/route.ts" +const listErc1155Balances = async (address: string) => { + const result = await avaCloudSDK.data.evm.balances.listErc1155Balances({ + pageSize: 10, + address: address, + }); + const balances: Erc1155TokenBalance[] = []; + for await (const page of result) { + balances.push(...page.result.erc1155TokenBalances); + } + return balances +} +``` + + + + + +Finally we will implement the `listRecentTransactions` function. The goal of this function is to fetch recent transactions for a given address given a block start and end. + + +Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-transactions/list-transactions) to see how to fetch recent transactions. +Note the `TransactionDetails` type that is imported, we should use this to combine and sort paged results. + + +```tsx title="src/app/api/wallet/route.ts" +const listRecentTransactions = async (address: string) => { + const blockHeight = await getBlockHeight() + const result = await avaCloudSDK.data.evm.transactions.listTransactions({ + pageSize: 10, + startBlock: blockHeight - 100000, + endBlock: blockHeight, + address: address, + sortOrder: "desc", + }); + const transactions: TransactionDetails = { + erc20Transfers: [], + erc721Transfers: [], + erc1155Transfers: [], + nativeTransaction: { + blockNumber: '', + blockTimestamp: 0, + blockHash: '', + blockIndex: 0, + txHash: '', + txStatus: '', + txType: 0, + gasLimit: '', + gasUsed: '', + gasPrice: '', + nonce: '', + from: { + name: undefined, + symbol: undefined, + decimals: undefined, + logoUri: undefined, + address: '' + }, + to: { + name: undefined, + symbol: undefined, + decimals: undefined, + logoUri: undefined, + address: '' + }, + value: '' + }, + } + for await (const page of result) { + for (const transaction of page.result.transactions) { + if (transaction.erc20Transfers) { + if (transactions.erc20Transfers) { + transactions.erc20Transfers.push(...transaction.erc20Transfers); + } + } + else if (transaction.erc721Transfers) { + if (transactions.erc721Transfers) { + transactions.erc721Transfers.push(...transaction.erc721Transfers); + } + } + else if (transaction.erc1155Transfers) { + if (transactions.erc1155Transfers) { + transactions.erc1155Transfers.push(...transaction.erc1155Transfers); + } + } + } + } + return transactions +} +``` + + + + + + +### Modify Frontend `src/app/basic-wallet/page.tsx` +Now we will modify the frontend to make calls to our backend. + + + + +First we will implement the `fetchERC20Balances` function. The goal of this function is to make a call to our backend to get the user's ERC-20 token balances. + + +Make a call to our backend first for the most recent block height, then our `listErc20Balances` API. Finally return the results. + + +```tsx title="src/app/basic-wallet/page.tsx" +const fetchERC20Balances = async (address: string) => { + const blockResult = await fetch("api/balance?method=getBlockHeight"); + const blockNumber = await blockResult.json(); + const balanceResult = await fetch("api/balance?method=listErc20Balances&address=" + address + "&blockNumber=" + blockNumber); + const balances = await balanceResult.json(); + return balances as Erc20TokenBalance[]; +}; +``` + + + + +Next we will implement the `fetchERC721Balances` function. The goal of this function is to call our `listErc721Balances` function on the backend and return it as a `Erc721TokenBalance` array. + + +Make a call to our backend then parse the result as json. Return it as `Erc721TokenBalance[]`. + + +```tsx title="src/app/basic-wallet/page.tsx" +const fetchERC721Balances = async (address: string) => { + const result = await fetch(`api/wallet?method=listERC721Balances&address=${address}`); + const balances = await result.json(); + return balances as Erc721TokenBalance[]; + } +``` + + + + + +Now we will implement the `fetchERC1155Balances` function. The goal of this function is to call our `listERC1155Balances` function on the backend and return it as a `Erc1155TokenBalance` array. + + +Make a call to our backend then parse the result as json. Return it as `Erc1155TokenBalance[]`. + + +```tsx title="src/app/basic-wallet/page.tsx" +const fetchERC1155Balances = async (address: string) => { + const result = await fetch(`api/wallet?method=listERC1155Balances&address=${address}`); + const balances = await result.json(); + return balances as Erc1155TokenBalance[]; + } +``` + + + + + +Finally we will implement the `fetchRecentTransactions` function. The goal of this function is to call our `listRecentTransactions` function on the backend and return it as an object of type `TransactionDetails`. + + +Make a call to our backend then parse the result as json. Return it as type of `TransactionDetails`. + + +```tsx title="src/app/basic-wallet/page.tsx" +const fetchRecentTransactions = async (address: string) => { + const result = await fetch(`api/wallet?method=listRecentTransactions&address=${address}`); + const transactions = await result.json(); + return transactions as TransactionDetails; + } +``` + + + + \ No newline at end of file diff --git a/content/course/avacloudsdk/06-block-explorer-app/01-overview.mdx b/content/course/avacloudsdk/06-block-explorer-app/01-overview.mdx new file mode 100644 index 00000000..5ef4ff03 --- /dev/null +++ b/content/course/avacloudsdk/06-block-explorer-app/01-overview.mdx @@ -0,0 +1,10 @@ +--- +title: Overview +description: Use the Data API to create a simple block explorer. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- +In this section we will build a basic block explorer using the Data API. + +![](/course-images/avacloudsdk/explorer.png) \ No newline at end of file diff --git a/content/course/avacloudsdk/06-block-explorer-app/02-understanding-code.mdx b/content/course/avacloudsdk/06-block-explorer-app/02-understanding-code.mdx new file mode 100644 index 00000000..47f69e72 --- /dev/null +++ b/content/course/avacloudsdk/06-block-explorer-app/02-understanding-code.mdx @@ -0,0 +1,7 @@ +--- +title: Understanding the Code +description: Lets modify the code to implement the Data API. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- diff --git a/content/course/avacloudsdk/06-block-explorer-app/03-modifying-code.mdx b/content/course/avacloudsdk/06-block-explorer-app/03-modifying-code.mdx new file mode 100644 index 00000000..a621dd58 --- /dev/null +++ b/content/course/avacloudsdk/06-block-explorer-app/03-modifying-code.mdx @@ -0,0 +1,7 @@ +--- +title: Modifying the Code +description: Before we start coding, let's take a look at the code we will be working with. +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- \ No newline at end of file diff --git a/content/course/avacloudsdk/07-using-webhooks/01-overview.mdx b/content/course/avacloudsdk/07-using-webhooks/01-overview.mdx new file mode 100644 index 00000000..2d215e30 --- /dev/null +++ b/content/course/avacloudsdk/07-using-webhooks/01-overview.mdx @@ -0,0 +1,7 @@ +--- +title: Overview +description: TBD +updated: 2024-09-13 +authors: [owenwahlgren] +icon: Book +--- diff --git a/content/course/avacloudsdk/index.mdx b/content/course/avacloudsdk/index.mdx new file mode 100644 index 00000000..94bdd654 --- /dev/null +++ b/content/course/avacloudsdk/index.mdx @@ -0,0 +1,35 @@ +--- +title: 👋 Welcome to the Course +description: Learn about AvaCloud APIs and the AvaCloud SDK. +updated: 2024-09-03 +authors: [owenwahlgren] +--- +![](/course-banner/avacloudsdk.jpg) +## Why Take This Course? + +[AvaCloud APIs](https://developers.avacloud.io/introduction), built by [AvaCloud](https://avacloud.io/), provides Web3 developers with multi-chain data from Avalanche’s primary network and other Avalanche L1s. With the AvaCloud API, you can easily build products that utilize real-time and historical transaction data, transfer records, native and token balances, and various types of token metadata. + +## Course Content + +- [AvaCloud API Overview](/course/avacloudsdk/02-glacier-overview/01-about-glacier) +- [Environment Setup](/course/avacloudsdk/03-environment-setup/01-avacloud-account) +- [Build an ERC-20 Token Balance App](/course/avacloudsdk/04-erc20-token-balance-app/01-overview) +- [Build a Wallet Portfolio App](/course/avacloudsdk/05-wallet-portfolio-app/01-overview) +- [Build a Basic Block Explorer](/course/avacloudsdk/06-block-explorer-app/01-overview) + +## Prerequisites + +A general understanding of web development is required. While you won't need to write a lot of code, familiarity with key concepts is important. + +- **TypeScript:** Understanding of common concepts, as all exercises will use TypeScript. +- **React:** Basic understanding of React is required, as all exercises will use React. +- **NextJS:** Basic understanding of NextJS is required, as all exercises will use NextJS. + +## Learning Outcomes + +By the end of this course, you will: + +- Be familiar with the AvaCloud API including the [Data API](https://developers.avacloud.io/data-api/overview) and [Webhooks API](https://developers.avacloud.io/webhooks-api/overview). +- Learn how to use the [AvaCloudSDK](https://github.com/ava-labs/avacloud-sdk-typescript) to interact with [AvaCloud APIs](https://developers.avacloud.io/introduction). +- Build multiple web apps using the [AvaCloudSDK](https://github.com/ava-labs/avacloud-sdk-typescript). + diff --git a/content/course/avacloudsdk/meta.json b/content/course/avacloudsdk/meta.json new file mode 100644 index 00000000..0d835ad2 --- /dev/null +++ b/content/course/avacloudsdk/meta.json @@ -0,0 +1,20 @@ +{ + "title": "Glacier", + "root": true, + "pages": [ + "index", + "---AvaCloud API Overview---", + "...02-glacier-overview", + "---Environment Setup---", + "...03-environment-setup", + "---Build an ERC-20 Balance App---", + "...04-erc20-token-balance-app", + "---Build a Wallet Portfolio---", + "...05-wallet-portfolio-app", + "---Build a Block Explorer---", + "...06-block-explorer-app", + "---Using Webhooks---", + "...07-using-webhooks" + ] + } + \ No newline at end of file diff --git a/content/course/interchain-messaging/10-running-a-relayer/04-relayer-configuration.mdx b/content/course/interchain-messaging/10-running-a-relayer/04-relayer-configuration.mdx index 55905632..c4103750 100644 --- a/content/course/interchain-messaging/10-running-a-relayer/04-relayer-configuration.mdx +++ b/content/course/interchain-messaging/10-running-a-relayer/04-relayer-configuration.mdx @@ -92,7 +92,7 @@ Next is the configuration for our source blockchain. This is the configuration o - **vm:** A string specifying the virtual machine of the destination L1's blockchain. Currently, only the EVM is supported, but this field has been added in anticipation of communication between blockchains powered by different virtual machines in the future. - **rpc-endpoint:** An API Config containing:
-- **base-url:** RPC endpoint of the destination L1's API node. +- **base-url:** RPC endpoint of the source L1's API node. - **query-parameters:** Additional query parameters to include in the API requests - **http-headers:** Additional HTTP headers to include in the API requests
@@ -110,7 +110,7 @@ Next is the configuration for our source blockchain. This is the configuration o ## Destination Blockchains -Next is the configuration for our source blockchain. This is the configuration of the blockchain where messages will be initiated and picked up. The relayer will aggregate the signatures of the validators of that L1. +Next is the configuration for our destination blockchain. This is the configuration of the blockchain where messages will be sent to. The relayer will aggregate the signatures of the validators of that L1. ```json { "destination-blockchains": [ @@ -143,4 +143,4 @@ For each destination L1, the relayer has the following config parameter: If you have been following all the course up to this point with the Avalanche-starter-kit and Avalanche-CLI will provide you with a relayer already funded to perform transactions between the local C-chain and your own chain -Some configuration fields have been omitted for the purpose of this exercise. If you are interested to read the extensive list of relayer configurations, you can visit the awm-relayer GitHub repository [here](https://github.com/ava-labs/awm-relayer/tree/main?tab=readme-ov-file#configuration). \ No newline at end of file +Some configuration fields have been omitted for the purpose of this exercise. If you are interested to read the extensive list of relayer configurations, you can visit the awm-relayer GitHub repository [here](https://github.com/ava-labs/awm-relayer/tree/main?tab=readme-ov-file#configuration). diff --git a/content/course/interchain-token-transfer/06-erc-20-to-erc-20-bridge/07-avacloud-and-core-bridge.mdx b/content/course/interchain-token-transfer/06-erc-20-to-erc-20-bridge/07-avacloud-and-core-bridge.mdx index e02c01ea..243e50a2 100644 --- a/content/course/interchain-token-transfer/06-erc-20-to-erc-20-bridge/07-avacloud-and-core-bridge.mdx +++ b/content/course/interchain-token-transfer/06-erc-20-to-erc-20-bridge/07-avacloud-and-core-bridge.mdx @@ -1,12 +1,12 @@ --- -title: AvaCloud, Core and ICTT +title: Integrate ICTT with Core description: Learn how to integrate ICTT bridges into the Core Bridge through AvaCloud. updated: 2024-05-31 icon: Book authors: [owenwahlgren] --- -## ICTT Bridge Inclusion in Core +## Integrate Interchain Token Transfers (ICTT) Into Core ICTT bridges deployed through [**AvaCloud**](https://avacloud.io/) will automatically integrate into the [**Core Bridge**](https://core.app/en/bridge). This ensures that any bridges created through AvaCloud are available immediately and do not need extra review. diff --git a/content/course/interchain-token-transfer/index.mdx b/content/course/interchain-token-transfer/index.mdx index fcb8b5f5..f52ff03c 100644 --- a/content/course/interchain-token-transfer/index.mdx +++ b/content/course/interchain-token-transfer/index.mdx @@ -12,7 +12,7 @@ In this course, you will learn how to transfer assets across multiple Avalanche A significant innovation in blockchain is the development of multi-chain systems, like Avalanche, which provide a significant improvement in scalability, interoperability, and flexibility. At the core of these multi-chain systems is the ability to run multiple blockchains that communicate. Each chain's VM is optimized for specialized use cases, thereby boosting the network's overall performance. -Cross-chain communication is a crucial building block of multi-chain systems. Utilizing Interchain Messaging and Avalanche Warp Messaging is an incredible easy way to build cross-Avalanche L1 dApps, since developers can build on top an extensive and audited development framework. +Cross-chain communication is a crucial building block of multi-chain systems. Utilizing Avalanche Interchain Messaging and Interchain Token Transfer is an incredibly easy way to build cross-Avalanche L1 dApps, since developers can build on top of an extensive, audited development framework. ## Course Content @@ -24,13 +24,13 @@ In the first section, we cover some basic concepts of interoperability in multi- In this section, we look at techniques to secure cross-chain communication. We dive into signature schemes, multi-signature schemes, and the BLS multi-signature scheme. -### Avalanche Warp Messaging (AWM) +### Interchain Messaging -Avalanche blockchains can natively interoperate between one another using AWM. You will learn about the AWM message format and how the message flow works. +Avalanche blockchains can natively interoperate between one another using ICM. You will learn about the ICM message format and how the message flow works. -### Teleporter +### Avalanche Interchain Token Transfer -In this section, we learn what Interchain Messaging is and what is abstracted away from the general dApp developer. You will also build your first cross-Avalanche L1 dApps. +In this section, we learn what Avalanche Interchain Token Transfer is and what is abstracted away from the general dApp developer. You will also build your first cross-Avalanche L1 dApps. ## Prerequisites diff --git a/content/course/multi-chain-architecture/04-independent-tokenomics/08-activate-native-minter.mdx b/content/course/multi-chain-architecture/04-independent-tokenomics/08-activate-native-minter.mdx index bd4f7e2b..888458cd 100644 --- a/content/course/multi-chain-architecture/04-independent-tokenomics/08-activate-native-minter.mdx +++ b/content/course/multi-chain-architecture/04-independent-tokenomics/08-activate-native-minter.mdx @@ -10,8 +10,8 @@ When prompted if you want to allow minting of new natie tokens, select **Yes**. ``` ? Allow minting of new native tokens?: - ▸ No, I want the supply of the native tokens be hard-capped - Yes, I want to be able to mint additional the native tokens (Native Minter Precompile ON) + No, I want the supply of the native tokens be hard-capped + ▸ Yes, I want to be able to mint additional the native tokens (Native Minter Precompile ON) ``` Let's add an address as an admin for our native token minting: @@ -63,4 +63,4 @@ Use the arrow keys to navigate: ↓ ↑ → ← ? Confirm?: ▸ Yes No, keep editing - ``` \ No newline at end of file + ``` diff --git a/content/courses.tsx b/content/courses.tsx index 6ab15a8b..bb34e006 100644 --- a/content/courses.tsx +++ b/content/courses.tsx @@ -98,7 +98,18 @@ const officialCourses: Course[] = [ tools: ["Avalanche-CLI"], languages: ["Go"], instructors: ["Martin Eckardt", "Ash"] // + Usman - } + }, + { + name:"AvaCloud APIs", + description:"Learn how to leverage AvaCloud APIs to build web apps on Avalanche", + slug:"avacloudsdk", + icon: , + duration: "1 hour", + featured: true, + tools: ["AvaCloudSDK", "AvaCloud API"], + languages: ["Typescript"], + instructors: ["Owen Wahlgren"] + } ]; const ecosystemCourses: Course[] = [ diff --git a/package-lock.json b/package-lock.json index da730a9e..6a0ce667 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@headlessui/react": "^2.1.2", "@heroicons/react": "^2.1.5", "@icons-pack/react-simple-icons": "^10.0.0", + "@noble/bls12-381": "^1.4.0", "@vercel/analytics": "^1.3.1", "@vercel/speed-insights": "^1.0.12", "fumadocs-core": "12.4.2", @@ -19,7 +20,8 @@ "geist": "^1.3.1", "idb": "^8.0.0", "lucide-react": "^0.408.0", - "next": "^14.2.4", + "next": "^14.2.12", + "pdf-lib": "^1.17.1", "react": "^18.3.1", "react-dom": "^18.3.1", "tailwindcss-animate": "^1.0.7", @@ -32,8 +34,11 @@ "@types/react-dom": "^18.3.0", "@types/uuid": "^10.0.0", "autoprefixer": "^10.4.19", + "axios": "^1.7.7", + "cheerio": "^1.0.0", "postcss": "^8.4.39", "tailwindcss": "^3.4.4", + "tsx": "^4.19.0", "typescript": "^5.5.3" } }, @@ -61,6 +66,390 @@ "node": ">=6.9.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@floating-ui/core": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", @@ -288,19 +677,17 @@ } }, "node_modules/@next/env": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.5.tgz", - "integrity": "sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==", - "license": "MIT" + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.12.tgz", + "integrity": "sha512-3fP29GIetdwVIfIRyLKM7KrvJaqepv+6pVodEbx0P5CaMLYBtx+7eEg8JYO5L9sveJO87z9eCReceZLi0hxO1Q==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz", - "integrity": "sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.12.tgz", + "integrity": "sha512-crHJ9UoinXeFbHYNok6VZqjKnd8rTd7K3Z2zpyzF1ch7vVNKmhjv/V7EHxep3ILoN8JB9AdRn/EtVVyG9AkCXw==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -310,9 +697,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz", - "integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.12.tgz", + "integrity": "sha512-JbEaGbWq18BuNBO+lCtKfxl563Uw9oy2TodnN2ioX00u7V1uzrsSUcg3Ep9ce+P0Z9es+JmsvL2/rLphz+Frcw==", "cpu": [ "x64" ], @@ -325,9 +712,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz", - "integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.12.tgz", + "integrity": "sha512-qBy7OiXOqZrdp88QEl2H4fWalMGnSCrr1agT/AVDndlyw2YJQA89f3ttR/AkEIP9EkBXXeGl6cC72/EZT5r6rw==", "cpu": [ "arm64" ], @@ -340,9 +727,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz", - "integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.12.tgz", + "integrity": "sha512-EfD9L7o9biaQxjwP1uWXnk3vYZi64NVcKUN83hpVkKocB7ogJfyH2r7o1pPnMtir6gHZiGCeHKagJ0yrNSLNHw==", "cpu": [ "arm64" ], @@ -355,9 +742,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz", - "integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.12.tgz", + "integrity": "sha512-iQ+n2pxklJew9IpE47hE/VgjmljlHqtcD5UhZVeHICTPbLyrgPehaKf2wLRNjYH75udroBNCgrSSVSVpAbNoYw==", "cpu": [ "x64" ], @@ -370,9 +757,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz", - "integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.12.tgz", + "integrity": "sha512-rFkUkNwcQ0ODn7cxvcVdpHlcOpYxMeyMfkJuzaT74xjAa5v4fxP4xDk5OoYmPi8QNLDs3UgZPMSBmpBuv9zKWA==", "cpu": [ "x64" ], @@ -385,9 +772,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz", - "integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.12.tgz", + "integrity": "sha512-PQFYUvwtHs/u0K85SG4sAdDXYIPXpETf9mcEjWc0R4JmjgMKSDwIU/qfZdavtP6MPNiMjuKGXHCtyhR/M5zo8g==", "cpu": [ "arm64" ], @@ -400,9 +787,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz", - "integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.12.tgz", + "integrity": "sha512-FAj2hMlcbeCV546eU2tEv41dcJb4NeqFlSXU/xL/0ehXywHnNpaYajOUvn3P8wru5WyQe6cTZ8fvckj/2XN4Vw==", "cpu": [ "ia32" ], @@ -415,9 +802,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz", - "integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.12.tgz", + "integrity": "sha512-yu8QvV53sBzoIVRHsxCHqeuS8jYq6Lrmdh0briivuh+Brsp6xjg80MAozUsBTAV9KNmY08KlX0KYTWz1lbPzEg==", "cpu": [ "x64" ], @@ -429,6 +816,18 @@ "node": ">= 10" } }, + "node_modules/@noble/bls12-381": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/bls12-381/-/bls12-381-1.4.0.tgz", + "integrity": "sha512-mIYqC2jMX7Lcu1QtU/FFftMPDSXNCdlGex6BSf5nPojHjnzzBgs1klFWpB1R8YjqHmOO9xrCzF19f2c42+z3vg==", + "deprecated": "Switch to @noble/curves for security updates", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -464,6 +863,22 @@ "node": ">= 8" } }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "dependencies": { + "pako": "^1.0.10" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1580,6 +1995,12 @@ "astring": "bin/astring" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/autoprefixer": { "version": "10.4.19", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", @@ -1618,6 +2039,17 @@ "postcss": "^8.1.0" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -1646,6 +2078,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1790,6 +2228,48 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2256,6 +2736,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -2295,6 +2787,34 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2344,6 +2864,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2384,6 +2913,61 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2403,6 +2987,70 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -2588,6 +3236,26 @@ "integrity": "sha512-W7cHV7Hrwjid6lWmy0IhsWDFQboWSng25U3VVywpHOTJnnAZNPScog67G+cVpeX9f7yDD21ih0WDrMMT+JoaYg==", "license": "Apache-2.0" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", @@ -2604,6 +3272,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -2766,6 +3448,18 @@ "node": ">=6" } }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", @@ -2933,6 +3627,37 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/idb": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz", @@ -4269,6 +4994,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -4338,12 +5084,11 @@ } }, "node_modules/next": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.5.tgz", - "integrity": "sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==", - "license": "MIT", + "version": "14.2.12", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.12.tgz", + "integrity": "sha512-cDOtUSIeoOvt1skKNihdExWMTybx3exnvbFbb9ecZDIxlvIbREQzt9A5Km3Zn3PfU+IFjyYGsHS+lN9VInAGKA==", "dependencies": { - "@next/env": "14.2.5", + "@next/env": "14.2.12", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -4358,15 +5103,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.5", - "@next/swc-darwin-x64": "14.2.5", - "@next/swc-linux-arm64-gnu": "14.2.5", - "@next/swc-linux-arm64-musl": "14.2.5", - "@next/swc-linux-x64-gnu": "14.2.5", - "@next/swc-linux-x64-musl": "14.2.5", - "@next/swc-win32-arm64-msvc": "14.2.5", - "@next/swc-win32-ia32-msvc": "14.2.5", - "@next/swc-win32-x64-msvc": "14.2.5" + "@next/swc-darwin-arm64": "14.2.12", + "@next/swc-darwin-x64": "14.2.12", + "@next/swc-linux-arm64-gnu": "14.2.12", + "@next/swc-linux-arm64-musl": "14.2.12", + "@next/swc-linux-x64-gnu": "14.2.12", + "@next/swc-linux-x64-musl": "14.2.12", + "@next/swc-win32-arm64-msvc": "14.2.12", + "@next/swc-win32-ia32-msvc": "14.2.12", + "@next/swc-win32-x64-msvc": "14.2.12" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -4473,6 +5218,18 @@ "url": "https://github.com/nebrelbug/npm-to-yarn?sponsor=1" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4497,6 +5254,11 @@ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/parse-entities": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", @@ -4523,6 +5285,43 @@ "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", "license": "MIT" }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4554,6 +5353,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pdf-lib/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -4760,6 +5575,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5031,6 +5852,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5064,6 +5894,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -5517,6 +6353,25 @@ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", + "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/typescript": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", @@ -5531,6 +6386,15 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "dev": true, + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -5783,6 +6647,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 45a81796..d44266e6 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "geist": "^1.3.1", "idb": "^8.0.0", "lucide-react": "^0.408.0", - "next": "^14.2.4", + "next": "^14.2.12", "pdf-lib": "^1.17.1", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/public/course-banner/avacloudsdk.jpg b/public/course-banner/avacloudsdk.jpg new file mode 100644 index 00000000..ac4bc1d0 Binary files /dev/null and b/public/course-banner/avacloudsdk.jpg differ diff --git a/public/course-images/avacloudsdk/balance-app-init.png b/public/course-images/avacloudsdk/balance-app-init.png new file mode 100644 index 00000000..49393d2a Binary files /dev/null and b/public/course-images/avacloudsdk/balance-app-init.png differ diff --git a/public/course-images/avacloudsdk/balance-app.png b/public/course-images/avacloudsdk/balance-app.png new file mode 100644 index 00000000..f2e6704d Binary files /dev/null and b/public/course-images/avacloudsdk/balance-app.png differ diff --git a/public/course-images/avacloudsdk/create-api-key.png b/public/course-images/avacloudsdk/create-api-key.png new file mode 100644 index 00000000..0fc0137a Binary files /dev/null and b/public/course-images/avacloudsdk/create-api-key.png differ diff --git a/public/course-images/avacloudsdk/explorer.png b/public/course-images/avacloudsdk/explorer.png new file mode 100644 index 00000000..f8eeef6d Binary files /dev/null and b/public/course-images/avacloudsdk/explorer.png differ diff --git a/public/course-images/avacloudsdk/final-api-key.png b/public/course-images/avacloudsdk/final-api-key.png new file mode 100644 index 00000000..8bf90a14 Binary files /dev/null and b/public/course-images/avacloudsdk/final-api-key.png differ diff --git a/public/course-images/avacloudsdk/glacier-data-api.png b/public/course-images/avacloudsdk/glacier-data-api.png new file mode 100644 index 00000000..02903aca Binary files /dev/null and b/public/course-images/avacloudsdk/glacier-data-api.png differ diff --git a/public/course-images/avacloudsdk/glacier-webhooks.png b/public/course-images/avacloudsdk/glacier-webhooks.png new file mode 100644 index 00000000..060e5eb1 Binary files /dev/null and b/public/course-images/avacloudsdk/glacier-webhooks.png differ diff --git a/public/course-images/avacloudsdk/grab-api-key.png b/public/course-images/avacloudsdk/grab-api-key.png new file mode 100644 index 00000000..22528a77 Binary files /dev/null and b/public/course-images/avacloudsdk/grab-api-key.png differ diff --git a/public/course-images/avacloudsdk/name-api-key.png b/public/course-images/avacloudsdk/name-api-key.png new file mode 100644 index 00000000..23f7d53c Binary files /dev/null and b/public/course-images/avacloudsdk/name-api-key.png differ diff --git a/public/course-images/avacloudsdk/nfts.png b/public/course-images/avacloudsdk/nfts.png new file mode 100644 index 00000000..8d5394c8 Binary files /dev/null and b/public/course-images/avacloudsdk/nfts.png differ diff --git a/public/course-images/avacloudsdk/preview.png b/public/course-images/avacloudsdk/preview.png new file mode 100644 index 00000000..e870a072 Binary files /dev/null and b/public/course-images/avacloudsdk/preview.png differ diff --git a/public/course-images/avacloudsdk/site-running.png b/public/course-images/avacloudsdk/site-running.png new file mode 100644 index 00000000..fb73f527 Binary files /dev/null and b/public/course-images/avacloudsdk/site-running.png differ diff --git a/public/course-images/avacloudsdk/terminal.png b/public/course-images/avacloudsdk/terminal.png new file mode 100644 index 00000000..c9018a7f Binary files /dev/null and b/public/course-images/avacloudsdk/terminal.png differ diff --git a/public/course-images/avacloudsdk/tokens.png b/public/course-images/avacloudsdk/tokens.png new file mode 100644 index 00000000..6daa1502 Binary files /dev/null and b/public/course-images/avacloudsdk/tokens.png differ diff --git a/public/course-images/avacloudsdk/vs-code.png b/public/course-images/avacloudsdk/vs-code.png new file mode 100644 index 00000000..ef1cf539 Binary files /dev/null and b/public/course-images/avacloudsdk/vs-code.png differ diff --git a/public/course-images/avacloudsdk/webhooks.jpg b/public/course-images/avacloudsdk/webhooks.jpg new file mode 100644 index 00000000..105a763a Binary files /dev/null and b/public/course-images/avacloudsdk/webhooks.jpg differ diff --git a/yarn.lock b/yarn.lock index ebfde55b..8041e3a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -275,55 +275,55 @@ unist-util-visit "^5.0.0" vfile "^6.0.0" -"@next/env@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.5.tgz#1d9328ab828711d3517d0a1d505acb55e5ef7ad0" - integrity sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA== - -"@next/swc-darwin-arm64@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz#d0a160cf78c18731c51cc0bff131c706b3e9bb05" - integrity sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ== - -"@next/swc-darwin-x64@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz#eb832a992407f6e6352eed05a073379f1ce0589c" - integrity sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA== - -"@next/swc-linux-arm64-gnu@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz#098fdab57a4664969bc905f5801ef5a89582c689" - integrity sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA== - -"@next/swc-linux-arm64-musl@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz#243a1cc1087fb75481726dd289c7b219fa01f2b5" - integrity sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA== - -"@next/swc-linux-x64-gnu@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz#b8a2e436387ee4a52aa9719b718992e0330c4953" - integrity sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ== - -"@next/swc-linux-x64-musl@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz#cb8a9adad5fb8df86112cfbd363aab5c6d32757b" - integrity sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ== - -"@next/swc-win32-arm64-msvc@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz#81f996c1c38ea0900d4e7719cc8814be8a835da0" - integrity sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw== - -"@next/swc-win32-ia32-msvc@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz#f61c74ce823e10b2bc150e648fc192a7056422e0" - integrity sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg== - -"@next/swc-win32-x64-msvc@14.2.5": - version "14.2.5" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz#ed199a920efb510cfe941cd75ed38a7be21e756f" - integrity sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g== +"@next/env@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.12.tgz#15f1d1065a420416e92f177fc8c94ee4ecc2669d" + integrity sha512-3fP29GIetdwVIfIRyLKM7KrvJaqepv+6pVodEbx0P5CaMLYBtx+7eEg8JYO5L9sveJO87z9eCReceZLi0hxO1Q== + +"@next/swc-darwin-arm64@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.12.tgz#263c68fd55538624a6236552d153a3487d601a33" + integrity sha512-crHJ9UoinXeFbHYNok6VZqjKnd8rTd7K3Z2zpyzF1ch7vVNKmhjv/V7EHxep3ILoN8JB9AdRn/EtVVyG9AkCXw== + +"@next/swc-darwin-x64@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.12.tgz#0fc05a99094ac531692d552743f62f7dbbcb5bc8" + integrity sha512-JbEaGbWq18BuNBO+lCtKfxl563Uw9oy2TodnN2ioX00u7V1uzrsSUcg3Ep9ce+P0Z9es+JmsvL2/rLphz+Frcw== + +"@next/swc-linux-arm64-gnu@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.12.tgz#56214b10cdb1c47d6f26ae2dd00bc9b32fd2a694" + integrity sha512-qBy7OiXOqZrdp88QEl2H4fWalMGnSCrr1agT/AVDndlyw2YJQA89f3ttR/AkEIP9EkBXXeGl6cC72/EZT5r6rw== + +"@next/swc-linux-arm64-musl@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.12.tgz#017ccb35e94dd5336f38bdab90ccc7163467e0d1" + integrity sha512-EfD9L7o9biaQxjwP1uWXnk3vYZi64NVcKUN83hpVkKocB7ogJfyH2r7o1pPnMtir6gHZiGCeHKagJ0yrNSLNHw== + +"@next/swc-linux-x64-gnu@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.12.tgz#b5df80780eceef6b44a0cedfe7234d34a60af1f9" + integrity sha512-iQ+n2pxklJew9IpE47hE/VgjmljlHqtcD5UhZVeHICTPbLyrgPehaKf2wLRNjYH75udroBNCgrSSVSVpAbNoYw== + +"@next/swc-linux-x64-musl@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.12.tgz#553ad8dd26e8fce343f2b01d741dffc8bb909e37" + integrity sha512-rFkUkNwcQ0ODn7cxvcVdpHlcOpYxMeyMfkJuzaT74xjAa5v4fxP4xDk5OoYmPi8QNLDs3UgZPMSBmpBuv9zKWA== + +"@next/swc-win32-arm64-msvc@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.12.tgz#cf9c3907f43b9a0cbe2f10a46f6c9f5de05ba9dc" + integrity sha512-PQFYUvwtHs/u0K85SG4sAdDXYIPXpETf9mcEjWc0R4JmjgMKSDwIU/qfZdavtP6MPNiMjuKGXHCtyhR/M5zo8g== + +"@next/swc-win32-ia32-msvc@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.12.tgz#cad79313b383e95e6d53bdb631b47a26e63146e0" + integrity sha512-FAj2hMlcbeCV546eU2tEv41dcJb4NeqFlSXU/xL/0ehXywHnNpaYajOUvn3P8wru5WyQe6cTZ8fvckj/2XN4Vw== + +"@next/swc-win32-x64-msvc@14.2.12": + version "14.2.12" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.12.tgz#9d5b2f2733221ae85c3e5c6d4b6f8f1da32d5cae" + integrity sha512-yu8QvV53sBzoIVRHsxCHqeuS8jYq6Lrmdh0briivuh+Brsp6xjg80MAozUsBTAV9KNmY08KlX0KYTWz1lbPzEg== "@noble/bls12-381@^1.4.0": version "1.4.0" @@ -2656,12 +2656,12 @@ next-themes@^0.3.0: resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.3.0.tgz#b4d2a866137a67d42564b07f3a3e720e2ff3871a" integrity sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w== -next@^14.2.4: - version "14.2.5" - resolved "https://registry.yarnpkg.com/next/-/next-14.2.5.tgz#afe4022bb0b752962e2205836587a289270efbea" - integrity sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA== +next@^14.2.12: + version "14.2.12" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.12.tgz#39d52c090c40980f4ae56f485ad234b777ebc955" + integrity sha512-cDOtUSIeoOvt1skKNihdExWMTybx3exnvbFbb9ecZDIxlvIbREQzt9A5Km3Zn3PfU+IFjyYGsHS+lN9VInAGKA== dependencies: - "@next/env" "14.2.5" + "@next/env" "14.2.12" "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" @@ -2669,15 +2669,15 @@ next@^14.2.4: postcss "8.4.31" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "14.2.5" - "@next/swc-darwin-x64" "14.2.5" - "@next/swc-linux-arm64-gnu" "14.2.5" - "@next/swc-linux-arm64-musl" "14.2.5" - "@next/swc-linux-x64-gnu" "14.2.5" - "@next/swc-linux-x64-musl" "14.2.5" - "@next/swc-win32-arm64-msvc" "14.2.5" - "@next/swc-win32-ia32-msvc" "14.2.5" - "@next/swc-win32-x64-msvc" "14.2.5" + "@next/swc-darwin-arm64" "14.2.12" + "@next/swc-darwin-x64" "14.2.12" + "@next/swc-linux-arm64-gnu" "14.2.12" + "@next/swc-linux-arm64-musl" "14.2.12" + "@next/swc-linux-x64-gnu" "14.2.12" + "@next/swc-linux-x64-musl" "14.2.12" + "@next/swc-win32-arm64-msvc" "14.2.12" + "@next/swc-win32-ia32-msvc" "14.2.12" + "@next/swc-win32-x64-msvc" "14.2.12" node-releases@^2.0.14: version "2.0.14" @@ -3161,7 +3161,16 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3187,7 +3196,14 @@ stringify-entities@^4.0.0: character-entities-html4 "^2.0.0" character-entities-legacy "^3.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==