diff --git a/.gitmodules b/.gitmodules index eea44da8c8..ad4d5dd0e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,6 +3,3 @@ url = https://github.com/OffchainLabs/arbitrum-sdk branch = main -[submodule "stylus-by-example"] - path = submodules/stylus-by-example - url = https://github.com/offchainlabs/stylus-by-example.git diff --git a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx index 2ea5737f97..d6558844ad 100644 --- a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx +++ b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx @@ -5,7 +5,7 @@ author: symbolpunk user_story: As a web2 developer, I want to onboard into Arbitrum by building and deploying my first smart contract, and knowing how to build a web widget interacting with it. content_type: quickstart slug: /build-decentralized-apps/quickstart-solidity-remix -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import { VanillaAdmonition } from '@site/src/components/VanillaAdmonition/'; diff --git a/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx b/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx index cf9614898f..4b4166a8e5 100644 --- a/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx +++ b/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx @@ -3,7 +3,7 @@ title: 'How to estimate gas in Arbitrum' description: Learn how to estimate gas before submitting transactions. author: TucksonDev content_type: how-to -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- :::info Looking for Stylus guidance? diff --git a/docs/build-decentralized-apps/03-public-chains.mdx b/docs/build-decentralized-apps/03-public-chains.mdx index bbc0b796a6..66d941d7b9 100644 --- a/docs/build-decentralized-apps/03-public-chains.mdx +++ b/docs/build-decentralized-apps/03-public-chains.mdx @@ -3,7 +3,7 @@ title: 'Arbitrum chains overview' description: A high level description of the Arbitrum chains available user_story: As a developer, I want to understand the different Arbitrum chains and how they relate to each other. content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import { AddressExplorerLink as AEL } from '@site/src/components/AddressExplorerLink'; diff --git a/docs/build-decentralized-apps/04-cross-chain-messaging.mdx b/docs/build-decentralized-apps/04-cross-chain-messaging.mdx index f73d98e710..c581bb7732 100644 --- a/docs/build-decentralized-apps/04-cross-chain-messaging.mdx +++ b/docs/build-decentralized-apps/04-cross-chain-messaging.mdx @@ -3,7 +3,7 @@ title: 'Cross-chain messaging overview' description: Learn about cross-chain messaging in Arbitrum user_story: As a developer, I want to understand how cross-chain messaging works in Arbitrum. content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- The Arbitrum protocol and related tooling makes it easy for developers to build cross-chain applications; i.e., applications that involve sending messages from Ethereum to an Arbitrum chain, and/or from an Arbitrum chain to Ethereum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx index 316fa8fdbc..595f405270 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx @@ -6,7 +6,7 @@ author: jose-franco sme: jose-franco target_audience: developers who want to build on Arbitrum content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- Arbitrum's design is to be as compatible and consistent with Ethereum as possible, from its high-level RPCs to its low-level bytecode and everything in between. Decentralized app developers with experience building on Ethereum will likely find that little to no new specific knowledge is required to build on Arbitrum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx index 80532d5abd..fdab098ba9 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx @@ -6,7 +6,7 @@ author: dzgoldman, jose-franco sme: jose-franco target_audience: developers who want to build on Arbitrum content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import { VanillaAdmonition } from '@site/src/components/VanillaAdmonition/'; diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx index 3c6314fc56..b70fb3de78 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx @@ -5,7 +5,7 @@ description: This concept page provides information about the differences betwee target_audience: developers who want to build on Arbitrum author: dzgoldman content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- Although the majority of RPC methods follow the same behavior as in Ethereum, some methods may produce a different result or add more information when used on an Arbitrum chain. This page covers the differences in response body fields you'll find when calling RPC methods on an Arbitrum chain vs on Ethereum. @@ -142,7 +142,7 @@ If the sync process encounters an error while trying to collect the data above t :::info The `cargo-stylus` command-line tool uses the `stylusTracer` to replay transactions locally inside a debugger. -More information can be found on [How to debug Stylus transactions using Cargo Stylus Replay](/stylus/how-tos/debugging-tx). +More information can be found on [How to debug Stylus transactions using Cargo Stylus Replay](/stylus/cli-tools/debugging-tx). ::: diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx index b5842e3010..9ed06b6a16 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx @@ -5,7 +5,7 @@ description: This concept page provides information about the differences betwee target_audience: developers who want to build on Arbitrum author: dzgoldman content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- Arbitrum chains are Ethereum-compatible and, therefore, allow you to trustlessly deploy Solidity smart contracts, as well as contracts written in Vyper or any other language that compiles to EVM bytecode. However, when calling certain properties and functions on a Solidity smart contract, there are some differences between the result you'd obtain if that contract were on Ethereum and the result on Arbitrum. diff --git a/docs/build-decentralized-apps/custom-gas-token-sdk.mdx b/docs/build-decentralized-apps/custom-gas-token-sdk.mdx index f100dac3cc..77c8f2c3f6 100644 --- a/docs/build-decentralized-apps/custom-gas-token-sdk.mdx +++ b/docs/build-decentralized-apps/custom-gas-token-sdk.mdx @@ -5,7 +5,7 @@ author: Mehdi Salehi sme: Mehdi Salehi target_audience: 'Developers deploying and maintaining Arbitrum chains.' sidebar_position: 2 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- Arbitrum SDK is a TypeScript library for client-side interactions with Arbitrum. It provides common helper functionality as well as access to the underlying smart contract interfaces. diff --git a/docs/build-decentralized-apps/nodeinterface/01-overview.mdx b/docs/build-decentralized-apps/nodeinterface/01-overview.mdx index 98aadcc4ae..0b2c8507a1 100644 --- a/docs/build-decentralized-apps/nodeinterface/01-overview.mdx +++ b/docs/build-decentralized-apps/nodeinterface/01-overview.mdx @@ -3,7 +3,7 @@ title: 'NodeInterface overview' description: A high level description of what the NodeInterface is and how it works user_story: As a developer, I want to understand what the NodeInterface is and how it works. content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- diff --git a/docs/build-decentralized-apps/nodeinterface/02-reference.mdx b/docs/build-decentralized-apps/nodeinterface/02-reference.mdx index 023bcc2db5..1be9333d80 100644 --- a/docs/build-decentralized-apps/nodeinterface/02-reference.mdx +++ b/docs/build-decentralized-apps/nodeinterface/02-reference.mdx @@ -3,7 +3,7 @@ title: 'NodeInterface reference' description: A reference page of the NodeInterface available on Arbitrum chains user_story: As a developer, I want to understand the specific methods available in the NodeInterface content_type: reference -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- The Arbitrum Nitro software includes a special `NodeInterface` contract available at address `0xc8` that is only accessible via RPCs (it's not actually deployed onchain, and thus can't be called by smart contracts). This reference page documents the specific calls available in the `NodeInterface`. For a more conceptual description of what it is and how it works, please refer to the [`NodeInterface` conceptual page](/build-decentralized-apps/nodeinterface/01-overview.mdx). diff --git a/docs/build-decentralized-apps/oracles/01-overview.mdx b/docs/build-decentralized-apps/oracles/01-overview.mdx index a4228fbe40..6f323431f6 100644 --- a/docs/build-decentralized-apps/oracles/01-overview.mdx +++ b/docs/build-decentralized-apps/oracles/01-overview.mdx @@ -5,7 +5,7 @@ description: A high level description of what oracles are user_story: As a developer, I want to understand what oracles are and how they work. content_type: concept sidebar_label: Oracles -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/precompiles/01-overview.mdx b/docs/build-decentralized-apps/precompiles/01-overview.mdx index 6b87a8c694..caad074ccd 100644 --- a/docs/build-decentralized-apps/precompiles/01-overview.mdx +++ b/docs/build-decentralized-apps/precompiles/01-overview.mdx @@ -3,7 +3,7 @@ title: 'Precompiles overview' description: A high level description of what precompiles are and how they work user_story: As a developer, I want to understand what precompiles are and how they work. content_type: concept -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- Precompiles are predefined smart contracts that have special addresses and provide specific functionality which is executed not at the EVM bytecode level, but natively by the Arbitrum client itself. Precompiles are primarily used to introduce specific functions that would be computationally expensive if executed in EVM bytecode, and functions that facilitate the interaction between the parent chain and the child chain. By having them natively in the Arbitrum client, they can be optimized for performance. diff --git a/docs/build-decentralized-apps/precompiles/02-reference.mdx b/docs/build-decentralized-apps/precompiles/02-reference.mdx index a382e6479c..e379246103 100644 --- a/docs/build-decentralized-apps/precompiles/02-reference.mdx +++ b/docs/build-decentralized-apps/precompiles/02-reference.mdx @@ -3,7 +3,7 @@ title: 'Precompiles reference' description: A reference page of all precompiles available on Arbitrum chains user_story: As a developer, I want to understand the most useful precompiles available on Arbitrum chains and how to use them. content_type: reference -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- ArbOS provides child chain-specific precompiles with methods smart contracts can call the same way they can solidity functions. This reference page exhaustively documents the specific calls ArbOS makes available through precompiles. For a more conceptual description of what precompiles are and how they work, please refer to the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx). diff --git a/docs/build-decentralized-apps/reference/01-node-providers.mdx b/docs/build-decentralized-apps/reference/01-node-providers.mdx index 1a6016b0cd..a3fd4af1d4 100644 --- a/docs/build-decentralized-apps/reference/01-node-providers.mdx +++ b/docs/build-decentralized-apps/reference/01-node-providers.mdx @@ -3,7 +3,7 @@ title: 'RPC endpoints and providers' description: Find available RPC endpoints and providers in the ecosystem reader_audience: developers who want to build on Arbitrum content_type: overview -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import ArbitrumRpcEndpoints from '../../partials/_reference-arbitrum-rpc-endpoints-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/02-contract-addresses.mdx b/docs/build-decentralized-apps/reference/02-contract-addresses.mdx index 01c4f451dd..5e91a09c55 100644 --- a/docs/build-decentralized-apps/reference/02-contract-addresses.mdx +++ b/docs/build-decentralized-apps/reference/02-contract-addresses.mdx @@ -6,7 +6,7 @@ author: anegg0 sme: anegg0 user_story: As a current or prospective Arbitrum user I need to know to what addresses Arbitrum contracts have been deployed. content_type: reference -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import ArbitrumContractAddresses from '../../partials/_reference-arbitrum-contract-addresses-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/03-chain-params.mdx b/docs/build-decentralized-apps/reference/03-chain-params.mdx index 470ac9385f..c01e6d529b 100644 --- a/docs/build-decentralized-apps/reference/03-chain-params.mdx +++ b/docs/build-decentralized-apps/reference/03-chain-params.mdx @@ -3,7 +3,7 @@ title: 'Chain parameters' description: Information about important system parameters for public Arbitrum chains user_story: As a developer, I want to understand the system parameters for the public Arbitrum chains. content_type: overview -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import ChainParameters from '../../partials/_reference-chain-parameters.mdx'; diff --git a/docs/build-decentralized-apps/reference/04-development-frameworks.mdx b/docs/build-decentralized-apps/reference/04-development-frameworks.mdx index ce41891626..a6a5475711 100644 --- a/docs/build-decentralized-apps/reference/04-development-frameworks.mdx +++ b/docs/build-decentralized-apps/reference/04-development-frameworks.mdx @@ -3,7 +3,7 @@ title: 'Development frameworks' description: An overview of popular development frameworks that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand the popular development frameworks that exist in the Arbitrum ecosystem. content_type: overview -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx b/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx index 103f5582f1..3915f5f3db 100644 --- a/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx +++ b/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx @@ -3,7 +3,7 @@ title: 'Web3 libraries and tools' description: An overview of some popular Web3 libraries that help developers interact with the Ethereum and Arbitrum blockchains. user_story: As a developer, I want to understand what Web3 libraries and tools are available in the Ethereum and Arbitrum ecosystems. content_type: overview -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx b/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx index 2b59378770..23c3a82c61 100644 --- a/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx +++ b/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx @@ -3,7 +3,7 @@ title: 'Monitoring tools and block explorers' description: An overview of popular monitoring tools and block explorers that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand what monitoring tools and block explorers are available in the Arbitrum ecosystem. content_type: overview -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/07-debugging-tools.mdx b/docs/build-decentralized-apps/reference/07-debugging-tools.mdx index 22fe44b703..17a64fb0f5 100644 --- a/docs/build-decentralized-apps/reference/07-debugging-tools.mdx +++ b/docs/build-decentralized-apps/reference/07-debugging-tools.mdx @@ -3,7 +3,7 @@ title: 'Debugging tools' description: An overview of popular debugging tools that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand what debugging tools are available in the Arbitrum ecosystem. content_type: overview -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx b/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx index 0431279295..219e3bae3f 100644 --- a/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx +++ b/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx @@ -2,7 +2,7 @@ title: 'Arbitrum: Understanding the risks' description: 'Understand the risks associated with cutting-edge software development' author: dzgoldman -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- # Arbitrum: Understanding the risks diff --git a/docs/build-decentralized-apps/token-bridging/01-overview.mdx b/docs/build-decentralized-apps/token-bridging/01-overview.mdx index 139e5b1ccd..0992179ad7 100644 --- a/docs/build-decentralized-apps/token-bridging/01-overview.mdx +++ b/docs/build-decentralized-apps/token-bridging/01-overview.mdx @@ -5,7 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how the token bridge works and what options exist to bridge assets between layers. content_type: overview sidebar_position: 1 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- Token bridging is a fundamental aspect of any Layer 2 (child chain) protocol. Arbitrum uses its ability to pass messages between parent and child chains (see [Cross-chain messaging](/build-decentralized-apps/04-cross-chain-messaging.mdx)) to enable projects to move assets between Ethereum and an Arbitrum chain trustlessly, and vice versa. Any asset and asset type in principle can be bridged, including `ETH`, `ERC-20` tokens, and `ERC-721` tokens, among others. diff --git a/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx b/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx index d6f4803f34..7a99151a64 100644 --- a/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx +++ b/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx @@ -5,7 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how bridging ether works on Arbitrum content_type: concept sidebar_position: 2 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx b/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx index cbaa2cfe8a..99a1eaa60c 100644 --- a/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx +++ b/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx @@ -5,7 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how ERC-20 token bridging works on Arbitrum, and the architecture of the token bridge. content_type: concept sidebar_position: 3 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx index 93ed153406..b35bd528c2 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx @@ -3,7 +3,7 @@ title: 'Get started with token bridging' description: Learn the different options available to bridge tokens programmatically user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum. content_type: overview -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- Token bridging is a fundamental aspect of any child chain protocol. It allows projects to quickly integrate with the Arbitrum ecosystem by leveraging their existing parent chain tokens. diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx index 3412398745..7679ab82d1 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx @@ -3,7 +3,7 @@ title: "Bridge tokens via Arbitrum's standard `ERC-20` gateway" description: Learn how to programmatically bridge tokens between Ethereum and Arbitrum using Arbitrum’s standard ER-C20 gateway user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum using the standard ER-C20 gateway. content_type: how-to -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- In this how-to, you’ll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum’s standard `ERC20` gateway](/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx#default-standard-bridging). For alternative ways of bridging tokens, don’t forget to check out this [overview](/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx). diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx index 81c8acd8f9..c1bf69c54a 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx @@ -3,7 +3,7 @@ title: 'Bridge tokens via Arbitrum’s generic-custom gateway' description: Learn how to use the generic-custom gateway to bridge tokens programmatically user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum using the generic-custom gateway content_type: how-to -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- In this how-to, you’ll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum’s generic-custom gateway](/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx#the-arbitrum-generic-custom-gateway). For alternative ways of bridging tokens, don’t forget to check out this [overview](/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx). diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx index 29b7fbb608..3ad4a6ea84 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx @@ -3,7 +3,7 @@ title: 'How to bridge tokens via a custom gateway' description: Learn how to set up a custom gateway using Arbitrum's Token Bridge to bridge tokens programmatically reader_audience: developers who want to build on Ethereum/Arbitrum and bridge tokens between layers content_type: how-to -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildSoliditySidebar --- :::caution Do you really need a custom gateway? diff --git a/docs/for-users/contribute.mdx b/docs/for-users/contribute.mdx new file mode 100644 index 0000000000..4f7c3501cf --- /dev/null +++ b/docs/for-users/contribute.mdx @@ -0,0 +1,8 @@ +--- +title: 'Contribute docs' +description: "Learn how to contribute to Arbitrum's documentation" +--- + +import ContributeDocsPartial from '../partials/_contribute-docs-partial.mdx'; + + diff --git a/docs/launch-arbitrum-chain/02-configure-your-chain/advanced-configurations/04-deploy-timeboost.mdx b/docs/launch-arbitrum-chain/02-configure-your-chain/advanced-configurations/04-deploy-timeboost.mdx new file mode 100644 index 0000000000..d871814667 --- /dev/null +++ b/docs/launch-arbitrum-chain/02-configure-your-chain/advanced-configurations/04-deploy-timeboost.mdx @@ -0,0 +1,195 @@ +--- +title: 'Deploy and configure Timeboost for your chain' +sidebar_label: 'Deploy and configure Timeboost' +description: 'Learn how to deploy and configure Timeboost' +user_story: As a current or prospective Orbit chain owner, I need to understand how to deploy and configure Timeboost +author: Jason Wan +sme: Jason Wan +content_type: how-to +--- + +# Enabling Timeboost for your Arbitrum (Orbit) chain + +This guide walks you through the process of enabling Timeboost for your Arbitrum Orbit chain. For a conceptual introduction to Timeboost, see the [Timeboost Introduction](/how-arbitrum-works/timeboost/gentle-introduction.mdx). + +## Prerequisites + +Before starting, ensure you have: + +1. An `ERC-20` token address to use as the bid token +2. A Redis server for auctioneer coordination +3. A server to run the auctioneer service +4. A proxy admin contract address + +## Overview + +Enabling Timeboost requires completing these three steps: + +1. Deploy the `ExpressLaneAuction` contract +2. Run Auctioneer Services (bid validator and auction server) +3. Configure your sequencer node to support Timeboost + +## Step 1: Deploy the `ExpressLaneAuction` contract + +First, clone the `orbit-actions` repository: + +```shell +git clone https://github.com/OffchainLabs/orbit-actions.git +cd orbit-actions/scripts/foundry/timeboost +``` + +Create and edit the environment configuration file: + +```shell +cp .env.sample .env +``` + +Configure the following parameters in your `.env` file: + +```shell +## Configuration for DeployExpressLaneAuction.s.sol +PROXY_ADMIN_ADDRESS= # Your proxy admin contract address +AUCTIONEER_ADDRESS= # Address that will send resolve auction requests +BIDDING_TOKEN_ADDRESS= # Your ERC20 bid token address +BENEFICIARY_ADDRESS= # Address to receive bid proceeds +AUCTIONEER_ADMIN_ADDRESS= # Admin address for the auctioneer +MIN_RESERVE_PRICE_SETTER_ADDRESS= # Address allowed to set minimum reserve price +RESERVE_PRICE_SETTER_ADDRESS= # Address allowed to set reserve price +RESERVE_PRICE_SETTER_ADMIN_ADDRESS= # Admin for reserve price setter +BENEFICIARY_SETTER_ADDRESS= # Address allowed to change beneficiary +ROUND_TIMING_SETTER_ADDRESS= # Address allowed to adjust round timing +MASTER_ADMIN_ADDRESS= # Master admin address + +MIN_RESERVE_PRICE=0 # Minimum price for bids (0 recommended for testing) + +# Round timing configuration (in seconds) +ROUND_DURATION_SECONDS=60 # Total duration of each round +AUCTION_CLOSING_SECONDS=15 # Time before round end when new bids are closed +RESERVE_SUBMISSION_SECONDS=15 # Time allocated for reserve price submission +``` + +Deploy the contract: + +```shell +forge script --sender $DEPLOYER --rpc-url $CHILD_CHAIN_RPC --slow ./DeployExpressLaneAuction.s.sol -vvv --verify --broadcast +# Use --account XXX / --private-key XXX / --interactive / --ledger to specify the transaction signer +``` + +Verify successful deployment by checking that the contract returns your configured bid token: + +```shell +cast call --rpc-url= "biddingToken()(address)" +``` + +Example output: + +```shell +0xYourBidTokenAddress +``` + +## Step 2: Run auctioneer services + +There are two distinct services to run: the bid validator and the auction server. The bid validator verifies submitted bids, while the auction server sends the winning bid on-chain. + +### Prerequisites + +The services require the `autonomous-auctioneer` binary, which is included in the Nitro Docker image. Alternatively, you can build it locally by following the [Build Nitro Locally](https://docs.arbitrum.io/run-arbitrum-node/nitro/build-nitro-locally) guide. + +To build only the `autonomous-auctioneer` component during the local build process: + +```shell +make target/bin/autonomous-auctioneer +``` + +### Running bid validator service + +Start the bid validator with: + +```shell +./autonomous-auctioneer \ +--bid-validator.auction-contract-address= \ +--bid-validator.rpc-endpoint= \ +--auctioneer-server.enable=false \ +--bid-validator.redis-url= \ +--http.addr=0.0.0.0 \ +--http.port= +``` + +### Running auction server service + +Start the auction server with: + +```shell +./autonomous-auctioneer \ +--auctioneer-server.auction-contract-address= \ +--auctioneer-server.db-directory= \ +--auctioneer-server.redis-url= \ +--auctioneer-server.use-redis-coordinator=false \ +--auctioneer-server.sequencer-endpoint= \ +--auctioneer-server.wallet.private-key= \ +--bid-validator.enable=false +``` + +## Step 3: Configure your sequencer node for Timeboost + +Update your sequencer node configuration to enable Timeboost functionality. Add the following new config to your sequencer's node configuration file: + +```json +{ + "http": { + "api": [ + // existing APIs + "auctioneer", + "timeboost" + ] + }, + "ws": { + "api": [ + // existing APIs + "auctioneer", + "timeboost" + ] + }, + "execution": { + "sequencer": { + "timeboost": { + "enable": true, + "auction-contract-address": "", + "auctioneer-address": "", + "redis-url": "" + } + } + } +} +``` + +## Verifying your Timeboost setup + +There are multiple ways to confirm that Timeboost is correctly enabled on your chain: + +### Periodic startup logs + +When you start your sequencer with Timeboost enabled, you'll see periodic logs indicating the start of new express lane auction rounds: + +```shell +New express lane auction round +``` + +This log indicates that the Timeboost mechanism is active and running normally. + +### Transaction processing confirmation + +After finishing a bid request, look for messages in your sequencer logs such as: + +```shell +AuctionResolved: New express lane controller assigned round +``` + +This message confirms that your sequencer is processing express queue transactions from the express lane controller, and that Timeboost is functioning correctly. + +### User interaction verification + +Users can interact with Timeboost by submitting bids through the `auctioneer_submitBid` endpoint of your auctioneer service. +For detailed instructions on how users can interact with Timeboost, see [How to Use Timeboost](https://docs.arbitrum.io/how-arbitrum-works/timeboost/how-to-use-timeboost). + +A successful bid submission will trigger the auction resolution process and generate the corresponding logs mentioned above. diff --git a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx index 8ec44c8492..699a81a7c1 100644 --- a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx +++ b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx @@ -1,22 +1,26 @@ --- -title: 'Bond and validator configurations' -description: 'Learn how to configure your Arbitrum chain with a custom bond and validator configurations' -author: pete-vielhaber -sme: Jason-W123 +title: 'Stake and validator configurations' +description: 'Learn how to configure your Arbitrum chain with a custom stake and validator configurations' +author: +sme: content_type: how-to +tags: ['hide-from-search'] unlisted: true --- -Arbitrum chains are customizable Layer 3 (L3) chains that settle -to an Arbitrum Layer 2 (L2) chain, such as Arbitrum One. They support - validator - configurations to ensure chain security through bonding and - assertion - challenges. Validators post assertions about the chain's state on the parent L2 chain and can - challenge - incorrect assertions. Arbitrum chains can be permissioned, meaning validators must be allowlisted. -For chains that use that elect to use the BoLD protocol, permissionless validation is an option (BoLD -also supports permissioned validation). +- **Base Stake**: Minimum bond required for validators. +- **Stake Token**: Token used for staking. +- **Loser Stake Escrow**: Address for escrowed funds from losing validators. + Arbitrum chains are customizable Layer 3 (L3) chains that settle + to an Arbitrum Layer 2 (L2) chain, such as Arbitrum One. They support + validator + configurations to ensure chain security through bonding and + assertion + challenges. Validators post assertions about the chain's state on the parent L2 chain and can + challenge + incorrect assertions. Arbitrum chains can be permissioned, meaning validators must be allowlisted. + For chains that use that elect to use the BoLD protocol, permissionless validation is an option (BoLD + also supports permissioned validation). Bonding is required for active validation, where validators place bond funds to participate. If a validator loses a challenge (e.g., due to a faulty assertion), their bond is escrowed or burned. Configurations such as the `stakeToken`, `baseStake`, and `loserStakeEscrow` are configurable during chain deployment or post-deployment via contract calls. diff --git a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx index 67eaeab929..f531d4b465 100644 --- a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx +++ b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx @@ -16,7 +16,7 @@ The specific upgrade requirements for each ArbOS release are located under each #### Step 1: Update Nitro on nodes and validators -Refer to the [requirements for the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx) to identify the specific [Nitro release](https://github.com/OffchainLabs/nitro/releases/) that supports the ArbOS version that you're upgrading to. For example, if your upgrade targets ArbOS 51, you'd use Nitro `v3.9.3` (Docker image: `offchainlabs/nitro-node:v3.9.3-8bc5554`) or higher. This is the version of the Nitro stack that needs to be running on each of your Arbitrum chain's nodes. A list of [all Nitro releases can be found on Github](https://github.com/OffchainLabs/nitro/releases). +Refer to the [requirements for the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx) to identify the specific [Nitro release](https://github.com/OffchainLabs/nitro/releases/) that supports the ArbOS version that you're upgrading to. For example, if your upgrade targets ArbOS 50, you'd use Nitro `v3.9.0` (Docker image: `offchainlabs/nitro-node:v3.9.0-cca645a`) or higher. This is the version of the Nitro stack that needs to be running on each of your Arbitrum chain's nodes. A list of [all Nitro releases can be found on Github](https://github.com/OffchainLabs/nitro/releases). Begin by upgrading your validator node(s) to the specified Nitro version, then update each remaining Arbitrum chain node to match this version. @@ -26,7 +26,7 @@ Note that upgrading your node version _must occur_ before the deadline establish While every ArbOS upgrade will require an update to the WASM module root, not every ArbOS upgrade will require an upgrade to the chain's `nitro-contracts` version. -If necessary, as defined in the release notes for each ArbOS release ([example of ArbOS 51](/run-arbitrum-node/arbos-releases/arbos51.mdx)), you may need to deploy new versions of some (or all) of the Nitro contracts to the parent chain of your Arbitrum chain. These contracts include the rollup logic, bridging logic, fraud-proof contracts, and interfaces for interacting with Nitro precompiles. To verify the current version of your Nitro contracts, follow [these instructions](https://github.com/OffchainLabs/orbit-actions/blob/main/README.md#check-version-and-upgrade-path) while replacing the inbox contract address and network name with that of your Arbitrum chain. This information will allow you to find the correct upgrade path for your Nitro contracts. +If necessary, as defined in the release notes for each ArbOS release ([example of ArbOS 50](/run-arbitrum-node/arbos-releases/arbos50.mdx)), you may need to deploy new versions of some (or all) of the Nitro contracts to the parent chain of your Arbitrum chain. These contracts include the rollup logic, bridging logic, fraud-proof contracts, and interfaces for interacting with Nitro precompiles. To verify the current version of your Nitro contracts, follow [these instructions](https://github.com/OffchainLabs/orbit-actions/blob/main/README.md#check-version-and-upgrade-path) while replacing the inbox contract address and network name with that of your Arbitrum chain. This information will allow you to find the correct upgrade path for your Nitro contracts. To update the WASM module root and deploy your chain's Nitro contracts to the parent chain for the most recent ArbOS release, you will need the following inputs (obtained from the [requirements for the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx)): @@ -35,7 +35,7 @@ To update the WASM module root and deploy your chain's Nitro contracts to the pa Once you have the WASM module root and have identified the required `nitro-contracts` version for the target ArbOS release, if any, [please follow the instructions in this guide](https://github.com/OffchainLabs/orbit-actions?tab=readme-ov-file#nitro-contracts-upgrades) for specific actions based on the `nitro-contracts` version you are deploying. Note that each ArbOS release will require performing this step with a different WASM module root and may require a different version of `nitro-contracts`. The guide linked above will be kept updated with the instructions for each specific ArbOS release. -The `WASM module root` is a 32-byte hash created from the Merkelized Go replay binary and its dependencies. When ArbOS is upgraded, a new WASM module root is generated due to modifications in the State Transition Function (STF). This new WASM module root must be set in the rollup contract on the parent chain. For example, the WASM module root for ArbOS 51 Dia is `0x8a7513bf7bb3e3db04b0d982d0e973bcf57bf8b88aef7c6d03dba3a81a56a499`. +The `WASM module root` is a 32-byte hash created from the Merkelized Go replay binary and its dependencies. When ArbOS is upgraded, a new WASM module root is generated due to modifications in the State Transition Function (STF). This new WASM module root must be set in the rollup contract on the parent chain. For example, the WASM module root for ArbOS 50 Dia is `0x2c54f6e9e378ba320ed9c713a1d9f067a572b1437e4f1c40b1a915d3066c04f2`. To set the WASM module root manually (i.e., not using the above guide), use the `Rollup proxy` contract's [`setWasmModuleRoot`](https://github.com/OffchainLabs/nitro-contracts/blob/38a70a5e14f8b52478eb5db08e7551a82ced14fe/src/rollup/RollupAdminLogic.sol#L321) method. Note that the `upgrade executor` contract on the parent chain is the designated owner of the Rollup contract, so the **chain owner account** needs to initiate a call to the `upgrade executor` contract in order to perform the upgrade. This call should include the correct calldata for setting the new WASM module root. @@ -49,7 +49,7 @@ WASM module roots are backward compatible, so upgrading them before an ArbOS ver To schedule an ArbOS version upgrade for your Arbitrum chain, [follow this guide](https://github.com/OffchainLabs/orbit-actions/tree/main/scripts/foundry/arbos-upgrades/at-timestamp). In addition to the upgrade action contract address and the account address for the chain owner account, you will need the following inputs: -1. **`newVersion`**: Specify the ArbOS version you wish to upgrade to (e.g., `51`). +1. **`newVersion`**: Specify the ArbOS version you wish to upgrade to (e.g., `50`). 2. **`timestamp`**: Set the exact UNIX timestamp at which you want your Arbitrum chain (Orbit) to transition to the new ArbOS version. If you would prefer to do this manually, simply call the [`scheduleArbOSUpgrade`](https://github.com/OffchainLabs/nitro-precompile-interfaces/blob/fe4121240ca1ee2cbf07d67d0e6c38015d94e704/ArbOwner.sol#L116) function on the `ArbOwner` precompile of the Arbitrum chain(s) you're upgrading. Because this is an administrative action (similar to upgrading your Wasm module root), the **chain owner account** must call the target chain's `upgrade executor` contract with the appropriate calldata in order to invoke the `scheduleArbOSUpgrade` function of the ArbOwner precompile. This will schedule the ArbOS upgrade using the specified version and timestamp. @@ -64,14 +64,14 @@ To upgrade immediately (without scheduling), set the timestamp to `0`. You can obtain the current ArbOS version of your chain by calling `ArbSys.ArbOSVersion()`. Keep in mind that this function adds `55` to the current ArbOS version. For example, if your chain is running on ArbOS 10, calling this function will return `65`. -When scheduling the ArbOS upgrade through `ArbOwner.scheduleArbOSUpgrade` you must use the actual ArbOS version you're upgrading to. For example, if you're upgrading to ArbOS 51, you will pass `51` when calling this function. +When scheduling the ArbOS upgrade through `ArbOwner.scheduleArbOSUpgrade` you must use the actual ArbOS version you're upgrading to. For example, if you're upgrading to ArbOS 50, you will pass `50` when calling this function. #### Step 4: Enable ArbOS specific configurations or feature flags (not always required) -For some ArbOS upgrades, such as [ArbOS 51 Dia](/run-arbitrum-node/arbos-releases/arbos51.mdx), there may be additional requirements or steps that need to be satisfied to ensure your Arbitrum chain can use all of the new features and improvements made available in that particular ArbOS release. +For some ArbOS upgrades, such as [ArbOS 50 Dia](/run-arbitrum-node/arbos-releases/arbos50.mdx), there may be additional requirements or steps that need to be satisfied to ensure your Arbitrum chain can use all of the new features and improvements made available in that particular ArbOS release. -If there are additional requirements for the targeted ArbOS release you're attempting to upgrade to; the additional requirements will be listed on the reference pages for [the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx#list-of-available-arbos-releases). For example, the additional requirements for Arbitrum chains upgrading to ArbOS 51 can be found [here on the ArbOS 51 docs](/run-arbitrum-node/arbos-releases/arbos51.mdx). +If there are additional requirements for the targeted ArbOS release you're attempting to upgrade to; the additional requirements will be listed on the reference pages for [the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx#list-of-available-arbos-releases). For example, the additional requirements for Arbitrum chains upgrading to ArbOS 50 can be found [here on the ArbOS 50 docs](/run-arbitrum-node/arbos-releases/arbos50.mdx). Congratulations! You've upgraded your Arbitrum chain(s) to the specified ArbOS version. diff --git a/docs/launch-arbitrum-chain/02-start-your-journey.mdx b/docs/launch-arbitrum-chain/02-start-your-journey.mdx new file mode 100644 index 0000000000..7efc94581b --- /dev/null +++ b/docs/launch-arbitrum-chain/02-start-your-journey.mdx @@ -0,0 +1,12 @@ +--- +title: 'Start your journey into Arbitrum chains' +sidebar_label: 'Arbitrum chain: get started' +description: 'Learn how to get started with Arbitrum chains' +sidebar_position: '2' +author: 'anegg0' +sme: 'mahsamoosavi' +editor: 'anegg0' +target_audience: 'Prospective chain owners, and operators' +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/03-arbitrum-license.mdx b/docs/launch-arbitrum-chain/03-arbitrum-license.mdx new file mode 100644 index 0000000000..b727357f1a --- /dev/null +++ b/docs/launch-arbitrum-chain/03-arbitrum-license.mdx @@ -0,0 +1,12 @@ +--- +title: 'The Arbitrum chain license' +sidebar_label: 'Arbitrum chain license' +description: '' +sidebar_position: 2 +author: +sme: +editor: anegg0 +target_audience: +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/01-bridging.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/01-bridging.mdx new file mode 100644 index 0000000000..598237adc9 --- /dev/null +++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/01-bridging.mdx @@ -0,0 +1,9 @@ +--- +title: 'Bridging' +description: 'PLACEHOLDER' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/02-monitoring.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/02-monitoring.mdx new file mode 100644 index 0000000000..649e63f45f --- /dev/null +++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/02-monitoring.mdx @@ -0,0 +1,9 @@ +--- +title: 'Monitoring' +description: 'Learn how to configure your Arbitrum chain with a custom bond and validator configurations' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/01-decentralization-security.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/01-decentralization-security.mdx new file mode 100644 index 0000000000..272aab0202 --- /dev/null +++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/01-decentralization-security.mdx @@ -0,0 +1,9 @@ +--- +title: 'Decentralization and Security' +description: 'PLACEHOLDER' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/02-guidance-on-altda.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/02-guidance-on-altda.mdx new file mode 100644 index 0000000000..eca5d9a361 --- /dev/null +++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/02-guidance-on-altda.mdx @@ -0,0 +1,9 @@ +--- +title: 'Guidance on AltDA' +description: 'PLACEHOLDER' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/06-third-party-integrations/03-integrations.mdx b/docs/launch-arbitrum-chain/06-third-party-integrations/03-integrations.mdx new file mode 100644 index 0000000000..34c95d1a99 --- /dev/null +++ b/docs/launch-arbitrum-chain/06-third-party-integrations/03-integrations.mdx @@ -0,0 +1,15 @@ +--- +title: 'Integrations' +description: 'PLACEHOLDER' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +unlisted: true +--- + +## Compatible third-party integrations + +## LayerZero OFT + +## xERC-20 Gateway diff --git a/docs/launch-arbitrum-chain/07-arbitrum-node-runners/arbitrum-chain-node-providers.mdx b/docs/launch-arbitrum-chain/07-arbitrum-node-runners/arbitrum-chain-node-providers.mdx new file mode 100644 index 0000000000..142b208a4a --- /dev/null +++ b/docs/launch-arbitrum-chain/07-arbitrum-node-runners/arbitrum-chain-node-providers.mdx @@ -0,0 +1,9 @@ +--- +title: 'Arbitrum chain Node Providers' +description: 'PLACEHOLDER' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +robots: noindex +--- diff --git a/docs/launch-arbitrum-chain/08-ecosystem-support/01-arbitrum-chain-portal.mdx b/docs/launch-arbitrum-chain/08-ecosystem-support/01-arbitrum-chain-portal.mdx new file mode 100644 index 0000000000..7c7c6b0fe6 --- /dev/null +++ b/docs/launch-arbitrum-chain/08-ecosystem-support/01-arbitrum-chain-portal.mdx @@ -0,0 +1,9 @@ +--- +title: 'Arbitrum chains Portal' +description: 'PLACEHOLDER' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/08-ecosystem-support/03-get-listed-arbitrum-chain-platforms.mdx b/docs/launch-arbitrum-chain/08-ecosystem-support/03-get-listed-arbitrum-chain-platforms.mdx new file mode 100644 index 0000000000..416349caf7 --- /dev/null +++ b/docs/launch-arbitrum-chain/08-ecosystem-support/03-get-listed-arbitrum-chain-platforms.mdx @@ -0,0 +1,9 @@ +--- +title: 'Get listed on Arbitrum platforms' +description: 'PLACEHOLDER' +author: +sme: +content_type: how-to +tags: ['hide-from-search'] +unlisted: true +--- diff --git a/docs/launch-arbitrum-chain/arbitrum-chain-supported-parent-chains.mdx b/docs/launch-arbitrum-chain/arbitrum-chain-supported-parent-chains.mdx new file mode 100644 index 0000000000..01cd8daf02 --- /dev/null +++ b/docs/launch-arbitrum-chain/arbitrum-chain-supported-parent-chains.mdx @@ -0,0 +1,60 @@ +--- +title: 'Supported parent chains' +sidebar_label: 'Supported parent chains' +description: 'List of officially supported parent chains for Arbitrum chains' +sidebar_position: '2' +author: 'mahsamoosavi' +sme: 'mahsamoosavi' +editor: 'anegg0' +target_audience: 'Arbitrum chains owners and operators' +--- + +This page lists the parent chains that are officially supported for Arbitrum chains, including mainnets, testnets, and options for local development. While the Arbitrum chain SDK provides functionality to enable custom parent chains, support is limited to the chains explicitly listed on this page. Developers are welcome to use the Arbitrum chain SDK to configure and deploy custom parent chains; however, such setups, including deploying the required creator and template contracts, are beyond the scope of our official support. + +:::caution Supported Chains Only + +Please note that we cannot guarantee compatibility or offer assistance for custom configurations outside of the supported chains detailed below. + +::: + +#### Supported chains + +
+ +- **Ethereum Mainnet** + **Chain ID:** `1` + +- **Arbitrum One** + **Chain ID:** `42161` + +- **Arbitrum Nova** + **Chain ID:** `42170` + +- **Base** + **Chain ID:** `8453` + +- **Sepolia** + **Chain ID:** `11155111` + +- **Arbitrum Sepolia** + **Chain ID:** `421613` + +- **Base Sepolia** + **Chain ID:** `84531` + +- **Nitro Testnode L1** + **Chain ID:** `1337` + +- **Nitro Testnode L2** + **Chain ID:** `412346` + +- **Nitro Testnode L3** + **Chain ID:** `333333` + +
+ +### Adding custom parent chains + +Although Arbitrum chains primarily supports a predefined set of chains, it provides developers with the flexibility to add custom parent chains through [the `registerCustomParentChain` function of the Arbitrum (Orbit) chain SDK](https://github.com/OffchainLabs/arbitrum-orbit-sdk/blob/729facd4d50156d7a84cf1204552c900eb86655c/src/chains.ts#L102). This capability enables integration with chains beyond the officially supported list, offering opportunities for customization and expanding the Arbtrium ecosystem. + +However, adding a custom chain requires deploying essential contracts, such as the creator contract and template contract, on the target chain. These contracts are fundamental for ensuring the proper functioning of the Arbitrum chain framework on the custom chain. diff --git a/docs/launch-arbitrum-chain/partials/_arbitrum-chain-public-preview-banner-partial.md b/docs/launch-arbitrum-chain/partials/_arbitrum-chain-public-preview-banner-partial.md new file mode 100644 index 0000000000..3b33fd052a --- /dev/null +++ b/docs/launch-arbitrum-chain/partials/_arbitrum-chain-public-preview-banner-partial.md @@ -0,0 +1,7 @@ +:::info PUBLIC PREVIEW, MAINNET READY + +Arbitrum chains are now [Mainnet ready](/launch-arbitrum-chain/concepts/public-preview-expectations.mdx#arbitrum-chains-are-mainnet-ready-but-deploy-to-testnet-first)! Note that Arbitrum is still a **[public preview](/launch-arbitrum-chain/concepts/public-preview-expectations.mdx)** capability - the Arbitrum product and its supporting documentation may change significantly as we capture feedback from readers like you. + +To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). + +::: diff --git a/docs/node-running/sequencer-content-map.mdx b/docs/node-running/sequencer-content-map.mdx deleted file mode 100644 index cd4465ec67..0000000000 --- a/docs/node-running/sequencer-content-map.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: sequencer-content-map -title: Sequencer -sidebar_label: Sequencer ---- - -import Card from '@site/src/components/Cards/Card'; - -# Sequencer - -Keep your node in sync with the sequencer. - -
- - - -
diff --git a/docs/partials/_macos-verify-tx-issue-banner-partial.mdx b/docs/partials/_macos-verify-tx-issue-banner-partial.mdx new file mode 100644 index 0000000000..11df1da02c --- /dev/null +++ b/docs/partials/_macos-verify-tx-issue-banner-partial.mdx @@ -0,0 +1,13 @@ +--- +partial_type: banner +title: 'MacOS Verification Issue Banner' +description: 'Warning banner for MacOS transaction verification limitations' +author: anegg0 +last_reviewed: 2025-01-15 +--- + +:::caution MacOS Users + +This feature is currently unavailable to MacOS users. We are working on a solution and will update this page when this limitation is resolved. + +::: diff --git a/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx b/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx index d191c83b9b..8ebb26465b 100644 --- a/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx +++ b/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx @@ -12,7 +12,7 @@ last_reviewed: 2025-01-15 - Unlike the RPC Urls, the Sequencer endpoints only support `eth_sendRawTransaction` and `eth_sendRawTransactionConditional` calls. - Arbitrum public RPCs do not provide Websocket support. -- View the [faucets](/stylus/reference/testnet-information.md#faucets) for testnet Sepolia tokens on L2. +- View the [faucets](docs/for-devs/dev-tools-and-resources/chain-info.mdx) for testnet Sepolia tokens on L2. ::: diff --git a/docs/partials/_under-construction-banner-partial.mdx b/docs/partials/_under-construction-banner-partial.mdx new file mode 100644 index 0000000000..1a30c582f3 --- /dev/null +++ b/docs/partials/_under-construction-banner-partial.mdx @@ -0,0 +1,15 @@ +--- +partial_type: banner +title: 'Under Construction Banner' +description: 'Banner indicating document is under construction' +author: anegg0 +last_reviewed: 2025-01-15 +--- + +:::caution UNDER CONSTRUCTION + +This document is currently under construction. + +If you're waiting for this content to be completed, click the `Request an update` button at the top of this page to let us know. We incorporate these requests into our content prioritization discussions. **Thank you!** + +::: diff --git a/docs/run-arbitrum-node/arbos-releases/arbos32.mdx b/docs/run-arbitrum-node/arbos-releases/arbos32.mdx index a4233c77fd..5ac1f9fb2d 100644 --- a/docs/run-arbitrum-node/arbos-releases/arbos32.mdx +++ b/docs/run-arbitrum-node/arbos-releases/arbos32.mdx @@ -42,8 +42,7 @@ ArbOS 32 Bianca brings many features, improvements, and bug fixes to Arbitrum ch It is strongly recommended that teams upgrading to ArbOS 32 also spend the time following the instructions described below to deploy and enable the Stylus Cache Manager. Even if your team does not intend to build with Stylus in the immediate term, enabling the Cache Manager ensures that future usage of Arbitrum Stylus on your chain is smooth and provides a consistent UX with the developer experience of building with Arbitrum Stylus on Arbitrum One. ::: -Specific to Stylus and ArbOS 32 "Bianca", we have developed a caching strategy that stores frequently accessed contracts in memory to reduce the costs and time associated with contract execution from repeated initializations. Check out the [Stylus caching strategy docs](../../stylus/how-tos/caching-contracts.mdx) to learn more. - +Specific to Stylus and ArbOS 32 "Bianca", we have developed a caching strategy that stores frequently accessed contracts in memory to reduce the costs and time associated with contract execution from repeated initializations. Check out the [Stylus caching strategy docs](../../stylus/guides/caching-contracts.mdx) to learn more. In order to take advantage of this caching strategy, an additional step is required to deploy and enable it's use on your Arbitrum chain. ### Additional requirement for Arbitrum chains who wish to enable Fast Withdrawals diff --git a/docs/stylus/advanced/_category_.yml b/docs/stylus/advanced/_category_.yml new file mode 100644 index 0000000000..2765a35f98 --- /dev/null +++ b/docs/stylus/advanced/_category_.yml @@ -0,0 +1,5 @@ +label: 'Advanced' +position: 8 +link: + type: generated-index + description: Deep dives into Stylus internals, advanced patterns, and optimization techniques. diff --git a/docs/stylus/advanced/hostio-exports.mdx b/docs/stylus/advanced/hostio-exports.mdx new file mode 100644 index 0000000000..80210af745 --- /dev/null +++ b/docs/stylus/advanced/hostio-exports.mdx @@ -0,0 +1,878 @@ +--- +title: 'Hostio exports' +description: 'Learn about low-level hostio functions for direct VM access in Stylus smart contracts' +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers who need to understand how to use hostio exports with Stylus. +displayed_sidebar: buildStylusSidebar +--- + +Hostio (Host I/O) exports are low-level functions that provide direct access to the Stylus VM runtime. These functions are WebAssembly imports that allow Stylus programs to interact with the blockchain environment, similar to how EVM opcodes work in Solidity. + +## Overview + +Hostio functions are the foundational layer that powers all Stylus smart contract operations. While most developers will use the higher-level SDK abstractions, understanding hostio functions is valuable for: + +- Performance optimization through direct VM access +- Implementing custom low-level operations +- Understanding gas costs and execution flow +- Debugging and troubleshooting contract behavior + +:::info +Most developers should use the high-level SDK wrappers instead of calling hostio functions directly. The SDK provides safe, ergonomic interfaces that handle memory management and error checking automatically. +::: + +## How hostio works + +Hostio functions are WebAssembly imports defined in the `vm_hooks` module. When a Stylus program is compiled to WASM, these functions are linked at runtime by the Arbitrum VM: + +```rust +#[link(wasm_import_module = "vm_hooks")] +extern "C" { + pub fn msg_sender(sender: *mut u8); + pub fn block_number() -> u64; + // ... more functions +} +``` + +During execution, calls to these functions are intercepted by the Stylus VM, which implements the actual functionality using the underlying ArbOS infrastructure. + +## Function categories + +Hostio functions are organized into several categories based on their purpose. + +### Account operations + +Query information about accounts on the blockchain. + +#### `account_balance` + +Gets the ETH balance of an account in wei. Equivalent to EVM's `BALANCE` opcode. + +```rust +pub fn account_balance(address: *const u8, dest: *mut u8); +``` + +**Parameters:** + +- `address`: Pointer to 20-byte address +- `dest`: Pointer to write 32-byte balance value + +**Usage:** + +```rust +use stylus_sdk::alloy_primitives::{Address, U256}; + +unsafe { + let addr = Address::from([0x11; 20]); + let mut balance_bytes = [0u8; 32]; + hostio::account_balance(addr.as_ptr(), balance_bytes.as_mut_ptr()); + let balance = U256::from_be_bytes(balance_bytes); +} +``` + +#### `account_code` + +Gets a subset of code from an account. Equivalent to EVM's `EXTCODECOPY` opcode. + +```rust +pub fn account_code( + address: *const u8, + offset: usize, + size: usize, + dest: *mut u8 +) -> usize; +``` + +**Returns:** Number of bytes actually written + +#### `account_code_size` + +Gets the size of code at an address. Equivalent to EVM's `EXTCODESIZE` opcode. + +```rust +pub fn account_code_size(address: *const u8) -> usize; +``` + +#### `account_codehash` + +Gets the code hash of an account. Equivalent to EVM's `EXTCODEHASH` opcode. + +```rust +pub fn account_codehash(address: *const u8, dest: *mut u8); +``` + +:::note +Empty accounts return the keccak256 hash of empty bytes: `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` +::: + +### Storage operations + +Interact with persistent contract storage. + +#### `storage_load_bytes32` + +Reads a 32-byte value from storage. Equivalent to EVM's `SLOAD` opcode. + +```rust +pub fn storage_load_bytes32(key: *const u8, dest: *mut u8); +``` + +**Parameters:** + +- `key`: Pointer to 32-byte storage key +- `dest`: Pointer to write 32-byte value + +#### `storage_cache_bytes32` + +Writes a 32-byte value to the storage cache. Equivalent to EVM's `SSTORE` opcode. + +```rust +pub fn storage_cache_bytes32(key: *const u8, value: *const u8); +``` + +:::warning +Values are cached and must be persisted using `storage_flush_cache` before they're permanently written to storage. +::: + +#### `storage_flush_cache` + +Persists cached storage values to the EVM state trie. Equivalent to multiple `SSTORE` operations. + +```rust +pub fn storage_flush_cache(clear: bool); +``` + +**Parameters:** + +- `clear`: Whether to drop the cache entirely after flushing + +**Storage caching benefits:** + +The Stylus VM implements storage caching for improved performance: + +- Repeated reads of the same key cost less gas +- Writes are batched for efficiency +- Cache is automatically managed by the SDK + +### Block information + +Access information about the current block. + +#### `block_basefee` + +Gets the basefee of the current block. Equivalent to EVM's `BASEFEE` opcode. + +```rust +pub fn block_basefee(basefee: *mut u8); +``` + +#### `block_chainid` + +Gets the chain identifier. Equivalent to EVM's `CHAINID` opcode. + +```rust +pub fn chainid() -> u64; +``` + +#### `block_coinbase` + +Gets the coinbase (block producer address). On Arbitrum, this is the L1 batch poster's address. + +```rust +pub fn block_coinbase(coinbase: *mut u8); +``` + +#### `block_gas_limit` + +Gets the gas limit of the current block. Equivalent to EVM's `GASLIMIT` opcode. + +```rust +pub fn block_gas_limit() -> u64; +``` + +#### `block_number` + +Gets a bounded estimate of the L1 block number when the transaction was sequenced. + +```rust +pub fn block_number() -> u64; +``` + +:::info +See [Arbitrum block numbers and time](https://developer.arbitrum.io/time) for more information on how block numbers work on Arbitrum chains. +::: + +#### `block_timestamp` + +Gets a bounded estimate of the Unix timestamp when the transaction was sequenced. + +```rust +pub fn block_timestamp() -> u64; +``` + +### Transaction and message context + +Access information about the current transaction and call context. + +#### `msg_sender` + +Gets the address of the caller. Equivalent to EVM's `CALLER` opcode. + +```rust +pub fn msg_sender(sender: *mut u8); +``` + +**Parameters:** + +- `sender`: Pointer to write 20-byte address + +:::note +For L1-to-L2 retryable ticket transactions, addresses are aliased. See [address aliasing documentation](https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing). +::: + +#### `msg_value` + +Gets the ETH value sent with the call in wei. Equivalent to EVM's `CALLVALUE` opcode. + +```rust +pub fn msg_value(value: *mut u8); +``` + +#### `msg_reentrant` + +Checks if the current call is reentrant. + +```rust +pub fn msg_reentrant() -> bool; +``` + +#### `tx_gas_price` + +Gets the gas price in wei per gas. On Arbitrum, this equals the basefee. Equivalent to EVM's `GASPRICE` opcode. + +```rust +pub fn tx_gas_price(gas_price: *mut u8); +``` + +#### `tx_origin` + +Gets the top-level sender of the transaction. Equivalent to EVM's `ORIGIN` opcode. + +```rust +pub fn tx_origin(origin: *mut u8); +``` + +#### `tx_ink_price` + +Gets the price of ink in EVM gas basis points. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/gas-metering) for more information. + +```rust +pub fn tx_ink_price() -> u32; +``` + +### Contract calls + +Make calls to other contracts. + +#### `call_contract` + +Calls another contract with optional value and gas limit. Equivalent to EVM's `CALL` opcode. + +```rust +pub fn call_contract( + contract: *const u8, + calldata: *const u8, + calldata_len: usize, + value: *const u8, + gas: u64, + return_data_len: *mut usize +) -> u8; +``` + +**Parameters:** + +- `contract`: Pointer to 20-byte contract address +- `calldata`: Pointer to calldata bytes +- `calldata_len`: Length of calldata +- `value`: Pointer to 32-byte value in wei (use 0 for no value) +- `gas`: Gas to supply (use `u64::MAX` for all available gas) +- `return_data_len`: Pointer to store length of return data + +**Returns:** `0` on success, non-zero on failure + +**Gas rules:** + +- Follows the 63/64 rule (at most 63/64 of available gas is forwarded) +- Includes callvalue stipend when value is sent + +**Usage:** + +```rust +use stylus_sdk::call::RawCall; + +unsafe { + let result = RawCall::new(self.vm()) + .gas(100_000) + .value(U256::from(1_000_000)) + .call(contract_address, &calldata)?; +} +``` + +#### `delegate_call_contract` + +Delegate calls another contract. Equivalent to EVM's `DELEGATECALL` opcode. + +```rust +pub fn delegate_call_contract( + contract: *const u8, + calldata: *const u8, + calldata_len: usize, + gas: u64, + return_data_len: *mut usize +) -> u8; +``` + +:::warning +Delegate calls execute code in the context of the current contract. Be extremely careful when delegate calling to untrusted contracts as they have full access to your storage. +::: + +#### `static_call_contract` + +Static calls another contract (read-only). Equivalent to EVM's `STATICCALL` opcode. + +```rust +pub fn static_call_contract( + contract: *const u8, + calldata: *const u8, + calldata_len: usize, + gas: u64, + return_data_len: *mut usize +) -> u8; +``` + +### Contract deployment + +Deploy new contracts. + +#### `create1` + +Deploys a contract using CREATE. Equivalent to EVM's `CREATE` opcode. + +```rust +pub fn create1( + code: *const u8, + code_len: usize, + endowment: *const u8, + contract: *mut u8, + revert_data_len: *mut usize +); +``` + +**Parameters:** + +- `code`: Pointer to initialization code (EVM bytecode) +- `code_len`: Length of initialization code +- `endowment`: Pointer to 32-byte value to send +- `contract`: Pointer to write deployed contract address (20 bytes) +- `revert_data_len`: Pointer to store revert data length on failure + +**Deployment rules:** + +- Init code must be EVM bytecode +- Deployed code can be Stylus (WASM) if it starts with `0xEFF000` header +- Address is determined by sender and nonce +- On failure, address will be zero + +#### `create2` + +Deploys a contract using CREATE2. Equivalent to EVM's `CREATE2` opcode. + +```rust +pub fn create2( + code: *const u8, + code_len: usize, + endowment: *const u8, + salt: *const u8, + contract: *mut u8, + revert_data_len: *mut usize +); +``` + +**Parameters:** + +- `salt`: Pointer to 32-byte salt value + +**Address calculation:** + +- Address is deterministic based on sender, salt, and init code hash +- Allows for pre-computed addresses + +### Events and logging + +Emit events to the blockchain. + +#### `emit_log` + +Emits an EVM log with topics and data. Equivalent to EVM's `LOG0`-`LOG4` opcodes. + +```rust +pub fn emit_log(data: *const u8, len: usize, topics: usize); +``` + +**Parameters:** + +- `data`: Pointer to event data (first bytes should be 32-byte aligned topics) +- `len`: Total length of data including topics +- `topics`: Number of topics (0-4) + +:::warning +Requesting more than 4 topics will cause a revert. +::: + +**Higher-level usage:** + +```rust +sol! { + event Transfer(address indexed from, address indexed to, uint256 value); +} + +// Emit using the SDK +self.vm().log(Transfer { + from: sender, + to: recipient, + value: amount, +}); +``` + +### Gas and ink metering + +Monitor execution costs. + +#### `evm_gas_left` + +Gets the amount of gas remaining. Equivalent to EVM's `GAS` opcode. + +```rust +pub fn evm_gas_left() -> u64; +``` + +#### `evm_ink_left` + +Gets the amount of ink remaining. Stylus-specific metering unit. + +```rust +pub fn evm_ink_left() -> u64; +``` + +:::info +Ink is Stylus's compute pricing unit. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/gas-metering) for conversion between ink and gas. +::: + +#### `pay_for_memory_grow` + +Pays for WASM memory growth. Automatically called when allocating new pages. + +```rust +pub fn pay_for_memory_grow(pages: u16); +``` + +:::note +The `entrypoint!` macro handles importing this hostio. Manual calls will unproductively consume gas. +::: + +### Cryptography + +Cryptographic operations. + +#### `native_keccak256` + +Efficiently computes keccak256 hash. Equivalent to EVM's `SHA3` opcode. + +```rust +pub fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8); +``` + +**Parameters:** + +- `bytes`: Pointer to input data +- `len`: Length of input data +- `output`: Pointer to write 32-byte hash + +**Higher-level usage:** + +```rust +use stylus_sdk::crypto::keccak; + +let hash = keccak(b"hello world"); +``` + +### Calldata operations + +Read and write calldata and return data. + +#### `read_args` + +Reads the program calldata. Equivalent to EVM's `CALLDATACOPY` opcode. + +```rust +pub fn read_args(dest: *mut u8); +``` + +:::note +This reads the entirety of the call's calldata. +::: + +#### `read_return_data` + +Copies bytes from the last call or deployment return result. Equivalent to EVM's `RETURNDATACOPY` opcode. + +```rust +pub fn read_return_data(dest: *mut u8, offset: usize, size: usize) -> usize; +``` + +**Parameters:** + +- `dest`: Destination buffer +- `offset`: Offset in return data to start copying from +- `size`: Number of bytes to copy + +**Returns:** Number of bytes actually written + +**Behavior:** + +- Does not revert if out of bounds +- Copies overlapping portion only + +#### `return_data_size` + +Gets the length of the last return result. Equivalent to EVM's `RETURNDATASIZE` opcode. + +```rust +pub fn return_data_size() -> usize; +``` + +#### `write_result` + +Writes the final return data for the current call. + +```rust +pub fn write_result(data: *const u8, len: usize); +``` + +**Behavior:** + +- Does not cause program to exit +- If not called, return data will be empty +- Program exits naturally when entrypoint returns + +#### `contract_address` + +Gets the address of the current program. Equivalent to EVM's `ADDRESS` opcode. + +```rust +pub fn contract_address(address: *mut u8); +``` + +### Debug and console + +Debug-only functions for development. + +#### `log_txt` + +Prints UTF-8 text to console. Only available in debug mode. + +```rust +pub fn log_txt(text: *const u8, len: usize); +``` + +#### `log_i32` / `log_i64` + +Prints integers to console. Only available in debug mode. + +```rust +pub fn log_i32(value: i32); +pub fn log_i64(value: i64); +``` + +#### `log_f32` / `log_f64` + +Prints floating-point numbers to console. Only available in debug mode with floating point enabled. + +```rust +pub fn log_f32(value: f32); +pub fn log_f64(value: f64); +``` + +**Higher-level usage:** + +```rust +use stylus_sdk::console; + +console!("Value: {}", value); // Prints in debug mode, no-op in production +``` + +## Safety considerations + +All hostio functions are marked `unsafe` because they: + +1. **Operate on raw pointers**: Require correct memory management +2. **Lack bounds checking**: Can cause undefined behavior if pointers are invalid +3. **Have side effects**: Can modify contract state or make external calls +4. **May revert**: Some operations can cause the transaction to revert + +### Safe usage patterns + +**Always validate inputs:** + +```rust +// Bad: unchecked pointer usage +unsafe { + hostio::msg_sender(ptr); // ptr might be invalid +} + +// Good: use safe wrappers +let sender = self.vm().msg_sender(); +``` + +**Use SDK wrappers:** + +```rust +// Bad: direct hostio call +unsafe { + let mut balance = [0u8; 32]; + hostio::account_balance(addr.as_ptr(), balance.as_mut_ptr()); +} + +// Good: use SDK wrapper +use stylus_sdk::evm; +let balance = evm::balance(addr); +``` + +**Handle return values:** + +```rust +// Check return status from calls +let status = unsafe { + hostio::call_contract( + contract.as_ptr(), + calldata.as_ptr(), + calldata.len(), + value.as_ptr(), + gas, + &mut return_len, + ) +}; + +if status != 0 { + // Handle call failure +} +``` + +## Higher-level wrappers + +The Stylus SDK provides safe, ergonomic wrappers around hostio functions: + +### Storage operations + +```rust +// Instead of direct hostio: +unsafe { + hostio::storage_load_bytes32(key.as_ptr(), dest.as_mut_ptr()); +} + +// Use storage types: +use stylus_sdk::storage::StorageU256; + +#[storage] +pub struct Contract { + value: StorageU256, +} + +let value = self.value.get(); // Safe, ergonomic +``` + +### Contract calls + +```rust +// Instead of direct hostio: +unsafe { + hostio::call_contract(/* many parameters */); +} + +// Use RawCall or typed interfaces: +use stylus_sdk::call::RawCall; + +let result = unsafe { + RawCall::new(self.vm()) + .gas(100_000) + .call(contract, &calldata)? +}; +``` + +### VM context + +```rust +// Instead of direct hostio: +unsafe { + let mut sender = [0u8; 20]; + hostio::msg_sender(sender.as_mut_ptr()); +} + +// Use VM accessor: +let sender = self.vm().msg_sender(); +let value = self.vm().msg_value(); +let timestamp = self.vm().block_timestamp(); +``` + +## Feature flags + +Hostio behavior changes based on feature flags: + +### `export-abi` + +When enabled, hostio functions are stubbed and return `unimplemented!()`. Used for ABI generation. + +### `stylus-test` + +When enabled, hostio functions panic with an error message. Use `TestVM` for testing instead. + +### `debug` + +When enabled, console logging functions become available. In production, console functions are no-ops. + +## Performance considerations + +### Direct hostio vs SDK wrappers + +- **Direct hostio**: Slightly lower overhead, requires manual memory management +- **SDK wrappers**: Minimal overhead (often zero-cost abstractions), much safer + +**Recommendation:** Use SDK wrappers unless profiling shows a specific performance bottleneck. + +### Storage caching + +The Stylus VM automatically caches storage operations: + +```rust +// First read: full SLOAD cost +let value1 = storage.value.get(); + +// Subsequent reads: reduced cost from cache +let value2 = storage.value.get(); + +// Writes are cached until flush +storage.value.set(new_value); // Cached + +// Cache is flushed automatically at call boundaries +``` + +### Gas vs ink + +Stylus uses "ink" for fine-grained gas metering: + +- **Ink**: WASM execution cost in Stylus-specific units +- **Gas**: Standard EVM gas units +- Conversion happens automatically + +Most developers don't need to think about ink vs gas distinction. + +## Common patterns + +### Check-effects-interactions pattern + +```rust +#[public] +impl MyContract { + pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), Vec> { + // Checks + let sender = self.vm().msg_sender(); + let balance = self.balances.get(sender); + if balance < amount { + return Err(b"Insufficient balance".to_vec()); + } + + // Effects + self.balances.setter(sender).set(balance - amount); + self.balances.setter(to).set(self.balances.get(to) + amount); + + // Interactions (if any) + Ok(()) + } +} +``` + +### Efficient event logging + +```rust +sol! { + event DataUpdated(bytes32 indexed key, uint256 value); +} + +// SDK handles hostio::emit_log internally +self.vm().log(DataUpdated { + key: key_hash, + value: new_value, +}); +``` + +### Gas-limited external calls + +```rust +use stylus_sdk::call::RawCall; + +// Limit gas to prevent griefing +let result = unsafe { + RawCall::new(self.vm()) + .gas(50_000) // Fixed gas limit + .call(untrusted_contract, &calldata) +}; + +match result { + Ok(data) => { /* process return data */ }, + Err(_) => { /* handle failure gracefully */ }, +} +``` + +## Testing with hostio + +Hostio functions are not available in the test environment. Use `TestVM` instead: + +```rust +#[cfg(test)] +mod tests { + use super::*; + use stylus_sdk::testing::*; + + #[test] + fn test_function() { + let vm = TestVM::default(); + let mut contract = MyContract::from(&vm); + + // VM functions work in tests + let sender = vm.msg_sender(); // Works + + // Direct hostio would panic + // unsafe { hostio::msg_sender(...) } // Would panic + } +} +``` + +## Resources + +- [Stylus VM specification](https://github.com/OffchainLabs/stylus) +- [EVM opcodes reference](https://www.evm.codes/) +- [Arbitrum block numbers and time](https://developer.arbitrum.io/time) +- [Ink and gas metering](https://docs.arbitrum.io/stylus/concepts/gas-metering) +- [stylus-sdk-rs source](https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/stylus-sdk/src/hostio.rs) + +## Best practices + +1. **Use SDK wrappers**: Prefer high-level abstractions over direct hostio calls +2. **Validate inputs**: Always check pointers and sizes before unsafe operations +3. **Handle errors**: Check return values from call operations +4. **Test thoroughly**: Use `TestVM` for comprehensive testing +5. **Profile first**: Only optimize to direct hostio if profiling shows it's necessary +6. **Document unsafe code**: Always document why `unsafe` is necessary +7. **Minimize unsafe blocks**: Keep `unsafe` blocks as small as possible diff --git a/docs/stylus/advanced/minimal-entrypoint-contracts.mdx b/docs/stylus/advanced/minimal-entrypoint-contracts.mdx new file mode 100644 index 0000000000..2168b20094 --- /dev/null +++ b/docs/stylus/advanced/minimal-entrypoint-contracts.mdx @@ -0,0 +1,644 @@ +--- +title: 'Minimal entrypoint contracts' +description: 'Understanding low-level Stylus contract entrypoints and the Router trait' +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers who need to understand how to build a minimal entrypoint contract without high-level macros. +displayed_sidebar: buildStylusSidebar +--- + +This guide explains the low-level mechanics of Stylus contract entrypoints, helping you understand what happens behind the `#[entrypoint]` and `#[public]` macros. This knowledge is useful for advanced use cases, debugging, and building custom contract frameworks. + +## Overview + +A Stylus contract at its core consists of: + +1. **`user_entrypoint` function**: The WASM export that Stylus calls +2. **Router implementation**: Routes function selectors to method implementations +3. **TopLevelStorage trait**: Marks the contract's root storage type +4. **ArbResult type**: Represents success/failure with encoded return data + +## Understanding `ArbResult` + +`ArbResult` is the fundamental return type for Stylus contract methods: + +```rust +pub type ArbResult = Result, Vec>; +``` + +- `Ok(Vec)` - Success with ABI-encoded return data +- `Err(Vec)` - Revert with ABI-encoded error data + +**Example:** + +```rust +use stylus_sdk::ArbResult; + +// Success with no return data +fn no_return() -> ArbResult { + Ok(Vec::new()) +} + +// Success with encoded data +fn return_value() -> ArbResult { + let value: u32 = 42; + Ok(value.to_le_bytes().to_vec()) +} + +// Revert with error data +fn revert_with_error() -> ArbResult { + Err(b"InsufficientBalance".to_vec()) +} +``` + +## The `user_entrypoint` Function + +The `user_entrypoint` function is the WASM export that Stylus calls when a transaction invokes the contract. The `#[entrypoint]` macro generates this function automatically. + +### Generated Structure + +When you use `#[entrypoint]`, the macro generates: + +```rust +#[no_mangle] +pub extern "C" fn user_entrypoint(len: usize) -> usize { + let host = stylus_sdk::host::VM { + host: stylus_sdk::host::WasmVM{} + }; + + // Reentrancy check (unless reentrant feature enabled) + if host.msg_reentrant() { + return 1; // revert + } + + // Ensure pay_for_memory_grow is referenced + // (costs 8700 ink, less than 1 gas) + host.pay_for_memory_grow(0); + + // Read calldata + let input = host.read_args(len); + + // Call the router + let (data, status) = match router_entrypoint::(input, host.clone()) { + Ok(data) => (data, 0), // Success + Err(data) => (data, 1), // Revert + }; + + // Persist storage changes + host.flush_cache(false); + + // Write return data + host.write_result(&data); + + status +} +``` + +### Key Points + +- **Signature**: `extern "C" fn user_entrypoint(len: usize) -> usize` +- **Input**: `len` is the length of calldata to read +- **Output**: `0` for success, `1` for revert +- **Side effects**: Reads calldata, writes return data, flushes storage cache + +## The `Router` Trait + +The `Router` trait defines how function calls are dispatched to method implementations. + +### Trait Definition + +```rust +pub trait Router +where + S: TopLevelStorage + BorrowMut + ValueDenier, + I: ?Sized, +{ + type Storage; + + /// Route a function call by selector + fn route(storage: &mut S, selector: u32, input: &[u8]) -> Option; + + /// Handle receive (plain ETH transfers, no calldata) + fn receive(storage: &mut S) -> Option>>; + + /// Handle fallback (unknown selectors or no receive) + fn fallback(storage: &mut S, calldata: &[u8]) -> Option; + + /// Handle constructor + fn constructor(storage: &mut S, calldata: &[u8]) -> Option; +} +``` + +### Routing Logic + +The `router_entrypoint` function implements the routing logic: + +```rust +pub fn router_entrypoint(input: Vec, host: VM) -> ArbResult +where + R: Router, + S: StorageType + TopLevelStorage + BorrowMut + ValueDenier, +{ + let mut storage = unsafe { S::new(U256::ZERO, 0, host) }; + + // No calldata - try receive, then fallback + if input.is_empty() { + if let Some(res) = R::receive(&mut storage) { + return res.map(|_| Vec::new()); + } + if let Some(res) = R::fallback(&mut storage, &[]) { + return res; + } + return Err(Vec::new()); // No receive or fallback + } + + // Extract selector (first 4 bytes) + if input.len() >= 4 { + let selector = u32::from_be_bytes(input[..4].try_into().unwrap()); + + // Check for constructor + if selector == CONSTRUCTOR_SELECTOR { + if let Some(res) = R::constructor(&mut storage, &input[4..]) { + return res; + } + } + // Try to route to a method + else if let Some(res) = R::route(&mut storage, selector, &input[4..]) { + return res; + } + } + + // Try fallback + if let Some(res) = R::fallback(&mut storage, &input) { + return res; + } + + Err(Vec::new()) // Unknown selector and no fallback +} +``` + +## The `TopLevelStorage` Trait + +The `TopLevelStorage` trait marks types that represent the contract's root storage. + +### Trait Definition + +```rust +pub unsafe trait TopLevelStorage {} +``` + +### Purpose + +- Prevents storage aliasing during reentrancy +- Lifetime tracks all EVM state changes during contract invocation +- Must hold a reference when making external calls +- Automatically implemented by `#[entrypoint]` + +### Safety + +The trait is `unsafe` because: + +- Type must truly be top-level to prevent storage aliasing +- Incorrectly implementing this trait can lead to undefined behavior + +## Building a Minimal Contract + +Here's a minimal contract without using the high-level macros: + +### Step 1: Define Storage + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloc::vec::Vec; +use stylus_sdk::{ + abi::{Router, ArbResult}, + storage::StorageType, + host::VM, + alloy_primitives::U256, +}; + +// Mark as top-level storage (normally done by #[entrypoint]) +pub struct MyContract; + +unsafe impl stylus_core::storage::TopLevelStorage for MyContract {} + +impl StorageType for MyContract { + type Wraps<'a> = &'a Self where Self: 'a; + type WrapsMut<'a> = &'a mut Self where Self: 'a; + + unsafe fn new(_slot: U256, _offset: u8, _host: VM) -> Self { + MyContract + } + + fn load<'s>(self) -> Self::Wraps<'s> { + &self + } + + fn load_mut<'s>(self) -> Self::WrapsMut<'s> { + &mut self + } +} +``` + +### Step 2: Implement Router + +```rust +impl Router for MyContract { + type Storage = MyContract; + + fn route(_storage: &mut MyContract, selector: u32, _input: &[u8]) -> Option { + // Simple example: one method with selector 0x12345678 + match selector { + 0x12345678 => Some(Ok(Vec::new())), + _ => None, // Unknown selector + } + } + + fn receive(_storage: &mut MyContract) -> Option>> { + None // No receive function + } + + fn fallback(_storage: &mut MyContract, _calldata: &[u8]) -> Option { + None // No fallback function + } + + fn constructor(_storage: &mut MyContract, _calldata: &[u8]) -> Option { + None // No constructor + } +} +``` + +### Step 3: Define Entrypoint + +```rust +#[no_mangle] +pub extern "C" fn user_entrypoint(len: usize) -> usize { + let host = VM { host: stylus_sdk::host::WasmVM{} }; + + // Reentrancy check + if host.msg_reentrant() { + return 1; + } + + // Reference pay_for_memory_grow + host.pay_for_memory_grow(0); + + // Read input + let input = host.read_args(len); + + // Route the call + let (data, status) = match stylus_sdk::abi::router_entrypoint::(input, host.clone()) { + Ok(data) => (data, 0), + Err(data) => (data, 1), + }; + + // Flush storage + host.flush_cache(false); + + // Write result + host.write_result(&data); + + status +} +``` + +## Function Selectors + +Function selectors are 4-byte identifiers computed from the function signature. + +### Computing Selectors + +```rust +use stylus_sdk::function_selector; + +// Manual computation +const MY_FUNCTION: [u8; 4] = function_selector!("myFunction"); + +// With parameters +const TRANSFER: [u8; 4] = function_selector!("transfer", Address, U256); + +// Constructor selector +const CONSTRUCTOR_SELECTOR: u32 = + u32::from_be_bytes(function_selector!("constructor")); +``` + +### Using in Router + +```rust +impl Router for MyContract { + type Storage = MyContract; + + fn route(_storage: &mut MyContract, selector: u32, input: &[u8]) -> Option { + const GET_VALUE: u32 = u32::from_be_bytes(function_selector!("getValue")); + const SET_VALUE: u32 = u32::from_be_bytes(function_selector!("setValue", U256)); + + match selector { + GET_VALUE => { + // Return encoded U256 value + let value = U256::from(42); + Some(Ok(value.to_be_bytes::<32>().to_vec())) + } + SET_VALUE => { + // Decode input and set value + if input.len() >= 32 { + // Process set_value logic + Some(Ok(Vec::new())) + } else { + Some(Err(Vec::new())) + } + } + _ => None, + } + } + + fn receive(_storage: &mut MyContract) -> Option>> { + None + } + + fn fallback(_storage: &mut MyContract, _calldata: &[u8]) -> Option { + None + } + + fn constructor(_storage: &mut MyContract, _calldata: &[u8]) -> Option { + None + } +} +``` + +## Implementing Special Functions + +### Receive Function + +Handles plain ETH transfers (no calldata): + +```rust +fn receive(storage: &mut MyContract) -> Option>> { + // Access msg_value via storage.vm().msg_value() + // Must return Ok(()) for success + Some(Ok(())) +} +``` + +### Fallback Function + +Handles unknown selectors or when no receive is defined: + +```rust +fn fallback(storage: &mut MyContract, calldata: &[u8]) -> Option { + // Can access full calldata + // Return Some to handle, None to revert + Some(Ok(Vec::new())) +} +``` + +### Constructor + +Called once during deployment with `CONSTRUCTOR_SELECTOR`: + +```rust +fn constructor(storage: &mut MyContract, calldata: &[u8]) -> Option { + // Initialize contract state + // calldata contains constructor parameters + Some(Ok(Vec::new())) +} +``` + +## Complete Minimal Example + +Here's a complete working minimal contract: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloc::vec::Vec; +use core::borrow::BorrowMut; +use stylus_sdk::{ + abi::Router, + alloy_primitives::U256, + host::VM, + storage::StorageType, + ArbResult, + function_selector, +}; +use stylus_core::{storage::TopLevelStorage, ValueDenier}; + +// Contract storage +pub struct MinimalContract; + +// Mark as top-level storage +unsafe impl TopLevelStorage for MinimalContract {} + +// Implement StorageType +impl StorageType for MinimalContract { + type Wraps<'a> = &'a Self where Self: 'a; + type WrapsMut<'a> = &'a mut Self where Self: 'a; + + unsafe fn new(_slot: U256, _offset: u8, _host: VM) -> Self { + MinimalContract + } + + fn load<'s>(self) -> Self::Wraps<'s> { + &self + } + + fn load_mut<'s>(self) -> Self::WrapsMut<'s> { + &mut self + } +} + +// Implement ValueDenier (for non-payable check) +impl ValueDenier for MinimalContract { + fn deny_value(&self, _method_name: &str) -> Result<(), Vec> { + Ok(()) // Allow all for simplicity + } +} + +// Implement BorrowMut +impl BorrowMut for MinimalContract { + fn borrow_mut(&mut self) -> &mut MinimalContract { + self + } +} + +// Implement Router +impl Router for MinimalContract { + type Storage = MinimalContract; + + fn route(_storage: &mut MinimalContract, selector: u32, _input: &[u8]) -> Option { + const HELLO: u32 = u32::from_be_bytes(function_selector!("hello")); + + match selector { + HELLO => Some(Ok(Vec::new())), + _ => None, + } + } + + fn receive(_storage: &mut MinimalContract) -> Option>> { + None + } + + fn fallback(_storage: &mut MinimalContract, _calldata: &[u8]) -> Option { + Some(Ok(Vec::new())) // Accept all unknown calls + } + + fn constructor(_storage: &mut MinimalContract, _calldata: &[u8]) -> Option { + Some(Ok(Vec::new())) + } +} + +// Define user_entrypoint +#[no_mangle] +pub extern "C" fn user_entrypoint(len: usize) -> usize { + let host = VM { host: stylus_sdk::host::WasmVM{} }; + + if host.msg_reentrant() { + return 1; + } + + host.pay_for_memory_grow(0); + + let input = host.read_args(len); + let (data, status) = match stylus_sdk::abi::router_entrypoint::(input, host.clone()) { + Ok(data) => (data, 0), + Err(data) => (data, 1), + }; + + host.flush_cache(false); + host.write_result(&data); + status +} +``` + +## Why Use High-Level Macros? + +While minimal contracts are educational, the `#[entrypoint]` and `#[public]` macros provide: + +1. **Automatic selector generation** from method names +2. **Type-safe parameter encoding/decoding** using Alloy types +3. **Solidity ABI export** for interoperability +4. **Storage trait implementations** with caching +5. **Error handling** with `Result` types +6. **Payable checks** for ETH-receiving functions +7. **Reentrancy protection** by default + +**Recommended approach:** + +```rust +// Use macros for production contracts +#[storage] +#[entrypoint] +pub struct MyContract { + value: StorageU256, +} + +#[public] +impl MyContract { + pub fn get_value(&self) -> U256 { + self.value.get() + } + + pub fn set_value(&mut self, value: U256) { + self.value.set(value); + } +} +``` + +This generates all the low-level code automatically while providing a clean, type-safe interface. + +## Advanced Use Cases + +### Custom Routing Logic + +Implement custom routing for multi-contract systems: + +```rust +impl Router for MultiContract { + type Storage = MultiContract; + + fn route(storage: &mut MultiContract, selector: u32, input: &[u8]) -> Option { + // Route to different modules based on selector range + match selector { + 0x00000000..=0x0fffffff => ModuleA::route(storage, selector, input), + 0x10000000..=0x1fffffff => ModuleB::route(storage, selector, input), + _ => None, + } + } + + // ... other methods +} +``` + +### Custom Entrypoint Logic + +Add custom logic before/after routing: + +```rust +#[no_mangle] +pub extern "C" fn user_entrypoint(len: usize) -> usize { + let host = VM { host: stylus_sdk::host::WasmVM{} }; + + // Custom pre-processing + let start_gas = host.evm_gas_left(); + + // Standard entrypoint logic + if host.msg_reentrant() { + return 1; + } + + host.pay_for_memory_grow(0); + let input = host.read_args(len); + + let (data, status) = match stylus_sdk::abi::router_entrypoint::(input, host.clone()) { + Ok(data) => (data, 0), + Err(data) => (data, 1), + }; + + // Custom post-processing + let gas_used = start_gas - host.evm_gas_left(); + // Log or handle gas usage + + host.flush_cache(false); + host.write_result(&data); + status +} +``` + +## Debugging Tips + +### Enable Debug Mode + +```rust +#[cfg(feature = "debug")] +use stylus_sdk::console; + +fn route(storage: &mut MyContract, selector: u32, input: &[u8]) -> Option { + #[cfg(feature = "debug")] + console!("Selector: {:08x}", selector); + + // Routing logic... +} +``` + +### Check Selector Computation + +```rust +#[test] +fn test_selectors() { + use stylus_sdk::function_selector; + + let hello = u32::from_be_bytes(function_selector!("hello")); + assert_eq!(hello, 0x19ff1d21); + + // Compare with Solidity: bytes4(keccak256("hello()")) +} +``` + +## See Also + +- [Contracts](../fundamentals/contracts.mdx) - High-level contract development +- [Global Variables](../fundamentals/global-variables-and-functions.mdx) - VM context methods +- [Storage Types](../fundamentals/data-types/storage.mdx) - Persistent storage diff --git a/docs/stylus/recommended-libraries.md b/docs/stylus/advanced/recommended-libraries.mdx similarity index 96% rename from docs/stylus/recommended-libraries.md rename to docs/stylus/advanced/recommended-libraries.mdx index 11f5485db1..0d7886f509 100644 --- a/docs/stylus/recommended-libraries.md +++ b/docs/stylus/advanced/recommended-libraries.mdx @@ -1,11 +1,9 @@ --- id: recommended-libraries -title: Recommended Libraries -sidebar_label: Use Rust Crates +title: Recommended libraries (Rust crates) +sidebar_label: Recommended libraries --- -# Recommended libraries - ## Using public Rust crates Rust provides a package registry at [crates.io](https://crates.io/), which lets developers conveniently access a plethora of open source libraries to utilize as dependencies in their code. Stylus Rust contracts can take advantage of these crates to simplify their development workflow. diff --git a/docs/stylus/advanced/rust-to-solidity-differences.mdx b/docs/stylus/advanced/rust-to-solidity-differences.mdx new file mode 100644 index 0000000000..18f813863e --- /dev/null +++ b/docs/stylus/advanced/rust-to-solidity-differences.mdx @@ -0,0 +1,745 @@ +--- +title: 'Rust to Solidity differences' +sidebar_label: 'Rust to Solidity' +description: 'Language and syntax differences between Rust Stylus and Solidity smart contracts' +user_story: 'As a Solidity developer, I want to understand how to write contracts in Rust' +content_type: concept +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers who need to compare Stylus with Solidity. +displayed_sidebar: buildStylusSidebar +--- + +Stylus introduces a new paradigm for writing smart contracts on Arbitrum using Rust and other WebAssembly-compatible languages. While Stylus contracts maintain full interoperability with Solidity contracts, there are important differences in how you structure and write code. This guide helps Solidity developers understand these differences. + +## Language and syntax + +### Contract structure + +**Solidity:** + +```solidity +contract MyContract { + uint256 private value; + address public owner; + + constructor(uint256 initialValue) { + value = initialValue; + owner = msg.sender; + } + + function setValue(uint256 newValue) public { + value = newValue; + } +} +``` + +**Stylus (Rust):** + +```rust +use stylus_sdk::prelude::*; +use stylus_sdk::alloy_primitives::{Address, U256}; + +#[storage] +#[entrypoint] +pub struct MyContract { + value: StorageU256, + owner: StorageAddress, +} + +#[public] +impl MyContract { + #[constructor] + pub fn constructor(&mut self, initial_value: U256) { + self.value.set(initial_value); + self.owner.set(self.vm().msg_sender()); + } + + pub fn set_value(&mut self, new_value: U256) { + self.value.set(new_value); + } +} +``` + +### Key structural differences + +1. **Attributes over keywords**: Stylus uses Rust attributes (`#[storage]`, `#[entrypoint]`, `#[public]`) instead of Solidity keywords +2. **Explicit storage types**: Storage variables use special types like `StorageU256`, `StorageAddress` +3. **Getter/setter pattern**: Storage access requires explicit `.get()` and `.set()` calls +4. **Module system**: Rust uses `mod` and `use` for imports instead of `import` + +## Function visibility and state mutability + +### Visibility + +**Solidity:** + +```solidity +function publicFunc() public {} +function externalFunc() external {} +function internalFunc() internal {} +function privateFunc() private {} +``` + +**Stylus:** + +```rust +#[public] +impl MyContract { + // Public external functions + pub fn public_func(&self) {} + + // Internal functions (not in #[public] block) + fn internal_func(&self) {} + + // Private functions + fn private_func(&self) {} +} +``` + +In Stylus: + +- Functions in `#[public]` blocks are externally callable +- Regular `pub fn` outside `#[public]` blocks are internal +- Non-pub functions are private to the module + +### State mutability + +**Solidity:** + +```solidity +function viewFunc() public view returns (uint256) {} +function pureFunc() public pure returns (uint256) {} +function payableFunc() public payable {} +``` + +**Stylus:** + +```rust +#[public] +impl MyContract { + // View function (immutable reference) + pub fn view_func(&self) -> U256 { + self.value.get() + } + + // Pure function (no self reference) + pub fn pure_func(a: U256, b: U256) -> U256 { + a + b + } + + // Payable function + #[payable] + pub fn payable_func(&mut self) { + // Can receive Ether + } + + // Write function (mutable reference) + pub fn write_func(&mut self) { + self.value.set(U256::from(42)); + } +} +``` + +State mutability in Stylus is determined by: + +- `&self` → View (read-only) +- `&mut self` → Write (can modify storage) +- No `self` → Pure (no storage access) +- `#[payable]` → Can receive Ether + +## Constructors + +**Solidity:** + +```solidity +constructor(uint256 initialValue) { + value = initialValue; +} +``` + +**Stylus:** + +```rust +#[public] +impl MyContract { + #[constructor] + pub fn constructor(&mut self, initial_value: U256) { + self.value.set(initial_value); + } +} +``` + +Key differences: + +- Use `#[constructor]` attribute +- Constructor name is always `constructor` +- Can be marked `#[payable]` if needed +- Called only once during deployment +- Each contract struct can have only one constructor + +## Modifiers + +Solidity modifiers don't exist in Stylus. Instead, use regular Rust patterns. + +**Solidity:** + +```solidity +modifier onlyOwner() { + require(msg.sender == owner, "Not owner"); + _; +} + +function sensitiveFunction() public onlyOwner { + // Function logic +} +``` + +**Stylus:** + +```rust +impl MyContract { + fn only_owner(&self) -> Result<(), Vec> { + if self.owner.get() != self.vm().msg_sender() { + return Err(b"Not owner".to_vec()); + } + Ok(()) + } +} + +#[public] +impl MyContract { + pub fn sensitive_function(&mut self) -> Result<(), Vec> { + self.only_owner()?; + // Function logic + Ok(()) + } +} +``` + +Or using custom errors: + +```rust +sol! { + error Unauthorized(); +} + +#[derive(SolidityError)] +pub enum MyErrors { + Unauthorized(Unauthorized), +} + +impl MyContract { + fn only_owner(&self) -> Result<(), MyErrors> { + if self.owner.get() != self.vm().msg_sender() { + return Err(MyErrors::Unauthorized(Unauthorized {})); + } + Ok(()) + } +} +``` + +## Fallback and receive functions + +**Solidity:** + +```solidity +receive() external payable { + // Handle plain Ether transfers +} + +fallback() external payable { + // Handle unmatched calls +} +``` + +**Stylus:** + +```rust +#[public] +impl MyContract { + #[receive] + #[payable] + pub fn receive(&mut self) -> Result<(), Vec> { + // Handle plain Ether transfers + Ok(()) + } + + #[fallback] + #[payable] + pub fn fallback(&mut self, calldata: &[u8]) -> ArbResult { + // Handle unmatched calls + Ok(Vec::new()) + } +} +``` + +Key differences: + +- Use `#[receive]` and `#[fallback]` attributes +- Receive function takes no parameters +- Fallback function receives calldata as a parameter +- Both return `Result` types + +## Events + +**Solidity:** + +```solidity +event Transfer(address indexed from, address indexed to, uint256 value); + +function transfer() public { + emit Transfer(msg.sender, recipient, amount); +} +``` + +**Stylus:** + +```rust +sol! { + event Transfer(address indexed from, address indexed to, uint256 value); +} + +#[public] +impl MyContract { + pub fn transfer(&mut self, recipient: Address, amount: U256) { + self.vm().log(Transfer { + from: self.vm().msg_sender(), + to: recipient, + value: amount, + }); + } +} +``` + +Key differences: + +- Define events in `sol!` macro +- Emit using `self.vm().log()` +- Up to 3 parameters can be indexed +- Can also use `raw_log()` for custom logging + +## Error handling + +**Solidity:** + +```solidity +error InsufficientBalance(uint256 requested, uint256 available); + +function withdraw(uint256 amount) public { + if (balance < amount) { + revert InsufficientBalance(amount, balance); + } +} +``` + +**Stylus:** + +```rust +sol! { + error InsufficientBalance(uint256 requested, uint256 available); +} + +#[derive(SolidityError)] +pub enum MyErrors { + InsufficientBalance(InsufficientBalance), +} + +#[public] +impl MyContract { + pub fn withdraw(&mut self, amount: U256) -> Result<(), MyErrors> { + let balance = self.balance.get(); + if balance < amount { + return Err(MyErrors::InsufficientBalance(InsufficientBalance { + requested: amount, + available: balance, + })); + } + Ok(()) + } +} +``` + +Key differences: + +- Define errors in `sol!` macro +- Create error enum with `#[derive(SolidityError)]` +- Return `Result` +- Use Rust's `?` operator for error propagation + +## Inheritance + +**Solidity:** + +```solidity +contract Base { + function baseFoo() public virtual {} +} + +contract Derived is Base { + function baseFoo() public override {} +} +``` + +**Stylus:** + +```rust +#[public] +trait IBase { + fn base_foo(&self); +} + +#[storage] +struct Base {} + +#[public] +impl IBase for Base { + fn base_foo(&self) { + // Implementation + } +} + +#[storage] +#[entrypoint] +struct Derived { + base: Base, +} + +#[public] +#[implements(IBase)] +impl Derived {} + +#[public] +impl IBase for Derived { + fn base_foo(&self) { + // Override implementation + } +} +``` + +Key differences: + +- Use Rust traits for interfaces +- Composition through storage fields +- Use `#[implements()]` to expose inherited interfaces +- No `virtual` or `override` keywords + +## Storage + +### Storage slots + +**Solidity:** + +```solidity +uint256 public value; +mapping(address => uint256) public balances; +uint256[] public items; +``` + +**Stylus:** + +```rust +#[storage] +pub struct MyContract { + value: StorageU256, + balances: StorageMap, + items: StorageVec, +} +``` + +### Storage access + +**Solidity:** + +```solidity +value = 42; +uint256 x = value; +balances[user] = 100; +``` + +**Stylus:** + +```rust +self.value.set(U256::from(42)); +let x = self.value.get(); +self.balances.setter(user).set(U256::from(100)); +``` + +Key differences: + +- Explicit storage types (`Storage*`) +- Must use `.get()` and `.set()` +- Maps use `.setter()` for write access +- Storage layout is compatible with Solidity + +## Constants and immutables + +**Solidity:** + +```solidity +uint256 public constant MAX_SUPPLY = 1000000; +address public immutable OWNER; + +constructor() { + OWNER = msg.sender; +} +``` + +**Stylus:** + +```rust +const MAX_SUPPLY: u64 = 1000000; + +#[storage] +#[entrypoint] +pub struct MyContract { + owner: StorageAddress, // Set in constructor +} + +#[public] +impl MyContract { + #[constructor] + pub fn constructor(&mut self) { + self.owner.set(self.vm().msg_sender()); + } +} +``` + +Key differences: + +- Use Rust `const` for constants +- No direct equivalent to `immutable` (use storage set once in constructor) +- Constants can be defined outside structs + +## Type system + +### Integer types + +| Solidity | Stylus (Rust) | Notes | +| -------------------- | ---------------------- | ------------------------------------- | +| `uint8` to `uint256` | `u8` to `u128`, `U256` | Native Rust types or Alloy primitives | +| `int8` to `int256` | `i8` to `i128`, `I256` | Signed integers | +| `address` | `Address` | 20-byte addresses | +| `bytes` | `Bytes` | Dynamic bytes | +| `bytesN` | `FixedBytes` | Fixed-size bytes | +| `string` | `String` | UTF-8 strings | + +### Arrays and mappings + +| Solidity | Stylus (Rust) | Notes | +| ----------------------------- | ------------------------------------------------------------ | -------------- | +| `uint256[]` | `Vec` (memory)
`StorageVec` (storage) | Dynamic arrays | +| `uint256[5]` | `[U256; 5]` | Fixed arrays | +| `mapping(address => uint256)` | `StorageMap` | Key-value maps | + +## Global variables and functions + +### Block and transaction properties + +| Solidity | Stylus (Rust) | +| ----------------- | ----------------------------- | +| `msg.sender` | `self.vm().msg_sender()` | +| `msg.value` | `self.vm().msg_value()` | +| `msg.data` | Access through calldata | +| `tx.origin` | `self.vm().tx_origin()` | +| `tx.gasprice` | `self.vm().tx_gas_price()` | +| `block.number` | `self.vm().block_number()` | +| `block.timestamp` | `self.vm().block_timestamp()` | +| `block.basefee` | `self.vm().block_basefee()` | +| `block.coinbase` | `self.vm().block_coinbase()` | + +### Cryptographic functions + +| Solidity | Stylus (Rust) | +| ----------------- | ---------------------------------- | +| `keccak256(data)` | `self.vm().native_keccak256(data)` | +| `sha256(data)` | Use external crate | +| `ecrecover(...)` | Use `crypto::recover()` | + +## External calls + +**Solidity:** + +```solidity +(bool success, bytes memory data) = address.call{value: amount}(data); +``` + +**Stylus:** + +```rust +use stylus_sdk::call::RawCall; + +let result = unsafe { + RawCall::new(self.vm()) + .value(amount) + .call(address, &data) +}; +``` + +Key differences: + +- Use `RawCall` for raw calls +- Calls are `unsafe` in Rust +- Use type-safe interfaces when possible via `sol_interface!` + +## Contract deployment + +**Solidity:** + +```solidity +new MyContract{value: amount}(arg1, arg2); +``` + +**Stylus:** + +```rust +use stylus_sdk::deploy::RawDeploy; + +let contract_address = unsafe { + RawDeploy::new(self.vm()) + .value(amount) + .deploy(&bytecode, salt)? +}; +``` + +## Assembly + +**Solidity:** + +```solidity +assembly { + let x := mload(0x40) + sstore(0, x) +} +``` + +**Stylus:** + +Stylus does not support inline assembly. Instead: + +- Use hostio functions for low-level operations +- Use Rust's `unsafe` blocks when necessary +- Direct memory manipulation through safe Rust APIs + +## Features not in Stylus + +1. **No inline assembly**: Use hostio or safe Rust instead +2. **No `selfdestruct`**: Deprecated in Ethereum, not available in Stylus +3. **No `delegatecall` from storage**: Available but requires careful use +4. **No modifier syntax**: Use regular functions +5. **No multiple inheritance complexity**: Use trait-based composition + +## Features unique to Stylus + +1. **Rust's type system**: Strong compile-time guarantees +2. **Zero-cost abstractions**: No overhead for safe code patterns +3. **Cargo ecosystem**: Access to thousands of Rust crates +4. **Memory safety**: Rust's borrow checker prevents common bugs +5. **Better performance**: Wasm execution can be more efficient +6. **Testing framework**: Use Rust's built-in testing with `TestVM` + +## Memory and gas costs + +### Memory management + +- **Solidity**: Automatic memory management with gas costs for allocation +- **Stylus**: Manual control with Rust's ownership system, more efficient memory usage + +### Gas efficiency + +Stylus programs typically use less gas than equivalent Solidity: + +- More efficient Wasm execution +- Better compiler optimizations +- Fine-grained control over allocations + +## Development workflow + +### Compilation + +**Solidity:** + +```shell +solc --bin --abi MyContract.sol +``` + +**Stylus:** + +```shell +cargo stylus build +``` + +### Testing + +**Solidity:** + +```javascript +// Hardhat or Foundry tests +``` + +**Stylus:** + +```rust +#[cfg(test)] +mod tests { + use super::*; + use stylus_sdk::testing::*; + + #[test] + fn test_function() { + let vm = TestVM::default(); + let mut contract = MyContract::from(&vm); + // Test logic + } +} +``` + +### Deployment + +Both use similar deployment processes but Stylus requires an activation step for new programs. + +## Interoperability + +Stylus and Solidity contracts can fully interact: + +- Stylus can call Solidity contracts +- Solidity can call Stylus contracts +- Same ABI encoding/decoding +- Share storage layout compatibility + +Example calling Solidity from Stylus: + +```rust +sol_interface! { + interface IToken { + function transfer(address to, uint256 amount) external returns (bool); + } +} + +#[public] +impl MyContract { + pub fn call_token(&self, token: Address, recipient: Address, amount: U256) -> Result> { + let token_contract = IToken::new(token); + let result = token_contract.transfer(self.vm(), recipient, amount)?; + Ok(result) + } +} +``` + +## Best practices for transitioning + +1. **Think in Rust patterns**: Don't translate Solidity directly, use idiomatic Rust +2. **Leverage the type system**: Use Rust's types to prevent bugs at compile time +3. **Use composition over inheritance**: Prefer traits and composition +4. **Handle errors explicitly**: Use `Result` types and the `?` operator +5. **Write tests in Rust**: Take advantage of `TestVM` for unit testing +6. **Read existing examples**: Study the stylus-sdk-rs examples directory +7. **Start small**: Convert simple contracts first to learn the patterns + +## Resources + +- [Stylus SDK Documentation](https://docs.arbitrum.io/stylus/reference/stylus-sdk) +- [stylus-sdk-rs Repository](https://github.com/OffchainLabs/stylus-sdk-rs) +- [Example Contracts](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/examples) +- [Rust Book](https://doc.rust-lang.org/book/) for learning Rust diff --git a/docs/stylus/best-practices/_category_.yml b/docs/stylus/best-practices/_category_.yml new file mode 100644 index 0000000000..401b1ad018 --- /dev/null +++ b/docs/stylus/best-practices/_category_.yml @@ -0,0 +1,5 @@ +label: 'Best practices' +position: 6 +link: + type: generated-index + description: Security, performance, and gas optimization patterns for production Stylus contracts. diff --git a/docs/stylus/best-practices/gas-optimization.mdx b/docs/stylus/best-practices/gas-optimization.mdx new file mode 100644 index 0000000000..e8c35dd56b --- /dev/null +++ b/docs/stylus/best-practices/gas-optimization.mdx @@ -0,0 +1,536 @@ +--- +title: 'Gas optimization best practices' +sidebar_label: 'Gas optimization' +description: 'Techniques and patterns for optimizing gas costs in Stylus smart contracts' +user_story: 'As a developer, I want to minimize gas costs for my Stylus contracts while maintaining functionality' +content_type: concept +author: offchainlabs +sme: offchainlabs +sidebar_position: 2 +--- + +Stylus contracts offer significant gas savings compared to Solidity (10-100x for compute-heavy operations), but following optimization best practices can reduce costs even further. + +## Why Stylus is cheaper + +![Gas Comparison](/img/stylus-gas-comparison.png) + +_Figure: Stylus WASM executes natively, avoiding EVM interpretation overhead._ + +### Performance comparison + +| Operation | Solidity (EVM) | Stylus (WASM) | Savings | +| ---------------------- | -------------- | ------------- | ----------- | +| Keccak256 hashing | ~30 gas/byte | ~3 gas/byte | **10x** | +| Signature verification | ~3,000 gas | ~300 gas | **10x** | +| Memory operations | ~3 gas/word | ~0.3 gas/word | **10x** | +| Compute-heavy loops | High | Very low | **50-100x** | +| Storage operations | Same | Same | **1x** | + +**Key insight**: Storage operations cost the same in Stylus and Solidity. Optimize by reducing storage access and maximizing compute efficiency. + +## Storage optimization + +### 1. Minimize storage reads + +```rust +// ❌ Bad: Multiple storage reads +pub fn calculate_bad(&self, iterations: u32) -> U256 { + let mut result = U256::ZERO; + for i in 0..iterations { + // Reads from storage every iteration! + result += self.multiplier.get(); + } + result +} + +// ✅ Good: Cache storage value +pub fn calculate_good(&self, iterations: u32) -> U256 { + // Read once, use many times + let multiplier = self.multiplier.get(); + + let mut result = U256::ZERO; + for i in 0..iterations { + result += multiplier; + } + result +} +``` + +**Gas impact**: Each storage read costs ~100 gas. The optimized version can save thousands of gas for large loops. + +### 2. Batch storage writes + +```rust +// ❌ Bad: Multiple separate writes +pub fn update_user_bad(&mut self, addr: Address, amount: U256, active: bool) { + self.balances.setter(addr).set(amount); + self.last_update.setter(addr).set(block::timestamp()); + self.is_active.setter(addr).set(active); +} + +// ✅ Good: Combine into struct +sol_storage! { + pub struct UserData { + U256 balance; + U256 last_update; + bool is_active; + } + + pub struct OptimizedContract { + StorageMap users; + } +} + +pub fn update_user_good(&mut self, addr: Address, amount: U256, active: bool) { + let mut user = self.users.setter(addr); + user.balance.set(amount); + user.last_update.set(block::timestamp()); + user.is_active.set(active); + // Single storage slot update instead of three +} +``` + +### 3. Use appropriate data types + +```rust +// ❌ Bad: Oversized types +sol_storage! { + pub struct Wasteful { + StorageU256 tiny_counter; // Only needs u8 + StorageU256 timestamp; // Only needs u64 + StorageU256 percentage; // Only needs u16 + } +} + +// ✅ Good: Right-sized types +sol_storage! { + pub struct Efficient { + StorageU8 tiny_counter; // Saves 31 bytes + StorageU64 timestamp; // Saves 24 bytes + StorageU16 percentage; // Saves 30 bytes + } +} +``` + +**Note**: While smaller types save storage space, they don't reduce gas for individual storage operations. The benefit comes from packing multiple small values in one slot (if your storage layout supports it). + +### 4. Delete unused storage + +```rust +pub fn cleanup(&mut self, addr: Address) -> Result<(), Vec> { + let balance = self.balances.get(addr); + + ensure!(balance == U256::ZERO, "Balance not zero"); + + // ✅ Deleting storage refunds gas + self.balances.delete(addr); + self.metadata.delete(addr); + + Ok(()) +} +``` + +**Gas refund**: Deleting storage refunds up to 15,000 gas per slot cleared. + +## Memory optimization + +### 1. Avoid unnecessary clones + +```rust +use alloy_primitives::Bytes; + +// ❌ Bad: Unnecessary cloning +pub fn process_data_bad(&self, data: Bytes) -> Bytes { + let copy = data.clone(); // Expensive memory allocation + copy +} + +// ✅ Good: Use references +pub fn process_data_good(&self, data: &Bytes) -> &Bytes { + data // No clone needed +} + +// ✅ Good: Move when possible +pub fn consume_data(mut data: Bytes) -> Bytes { + data.extend_from_slice(&[1, 2, 3]); + data // Ownership moved, no clone +} +``` + +### 2. Use iterators efficiently + +```rust +// ❌ Bad: Collect into vector unnecessarily +pub fn sum_bad(&self, values: Vec) -> U256 { + let filtered: Vec = values + .iter() + .filter(|v| **v > U256::ZERO) + .copied() + .collect(); // Allocates new vector + + filtered.iter().sum() +} + +// ✅ Good: Chain iterators +pub fn sum_good(&self, values: Vec) -> U256 { + values + .iter() + .filter(|v| **v > U256::ZERO) + .sum() // No intermediate allocation +} +``` + +### 3. Reuse allocations + +```rust +// ✅ Reuse buffers for repeated operations +pub fn process_batch(&mut self, items: Vec) -> Vec { + let mut buffer = Vec::with_capacity(items.len()); + + for item in items { + buffer.clear(); // Reuse allocation + buffer.extend_from_slice(&item); + // Process buffer... + } + + buffer +} +``` + +## Computation optimization + +### 1. Use Stylus for compute-heavy operations + +```rust +// ✅ Stylus excels at complex computation +pub fn verify_merkle_proof( + &self, + leaf: [u8; 32], + proof: Vec<[u8; 32]>, + root: [u8; 32] +) -> bool { + let mut computed_hash = leaf; + + // This loop is 10-50x cheaper in Stylus than Solidity + for proof_element in proof { + computed_hash = if computed_hash <= proof_element { + keccak256(&[computed_hash, proof_element].concat()) + } else { + keccak256(&[proof_element, computed_hash].concat()) + }; + } + + computed_hash == root +} +``` + +**Why it's faster**: Native WASM execution vs. EVM interpretation makes loops dramatically cheaper. + +### 2. Optimize hot paths + +```rust +// ✅ Optimize frequently-called functions +#[inline(always)] +pub fn is_valid_amount(&self, amount: U256) -> bool { + amount > U256::ZERO && amount <= self.max_amount.get() +} + +// Use in hot path +pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), Vec> { + ensure!(self.is_valid_amount(amount), "Invalid amount"); + // Transfer logic... + Ok(()) +} +``` + +### 3. Avoid redundant checks + +```rust +// ❌ Bad: Redundant zero check +pub fn add_to_balance_bad(&mut self, addr: Address, amount: U256) -> Result<(), Vec> { + ensure!(amount > U256::ZERO, "Amount must be positive"); + + let current = self.balances.get(addr); + ensure!(current + amount > current, "Overflow"); // Redundant if amount > 0 + + self.balances.setter(addr).add_assign(amount); + Ok(()) +} + +// ✅ Good: Single overflow check covers both +pub fn add_to_balance_good(&mut self, addr: Address, amount: U256) -> Result<(), Vec> { + let current = self.balances.get(addr); + + let new_balance = current + .checked_add(amount) + .ok_or("Overflow or invalid amount")?; + + self.balances.setter(addr).set(new_balance); + Ok(()) +} +``` + +## Function call optimization + +### 1. Minimize cross-contract calls + +```rust +// ❌ Bad: Multiple external calls +pub fn get_price_bad(&self, token: Address) -> Result> { + let oracle = IOracle::new(self.oracle_address.get()); + + let price = oracle.get_price(self, token)?; + let decimals = oracle.get_decimals(self, token)?; // Second call + let timestamp = oracle.get_timestamp(self, token)?; // Third call + + Ok(price) +} + +// ✅ Good: Batch external calls +pub fn get_price_good(&self, token: Address) -> Result> { + let oracle = IOracle::new(self.oracle_address.get()); + + // Single call returns all data + oracle.get_price_data(self, token) +} +``` + +**Gas impact**: Each external call has overhead. Batching reduces cost significantly. + +### 2. Use internal functions + +```rust +// ✅ Extract common logic to internal functions +impl MyContract { + // Internal helper (no ABI encoding overhead) + fn internal_validate(&self, addr: Address, amount: U256) -> Result<(), Vec> { + ensure!(!addr.is_zero(), "Invalid address"); + ensure!(amount > U256::ZERO, "Invalid amount"); + Ok(()) + } + + // Public functions use internal helper + #[external] + pub fn deposit(&mut self, amount: U256) -> Result<(), Vec> { + self.internal_validate(msg::sender(), amount)?; + // Deposit logic... + Ok(()) + } + + #[external] + pub fn withdraw(&mut self, amount: U256) -> Result<(), Vec> { + self.internal_validate(msg::sender(), amount)?; + // Withdraw logic... + Ok(()) + } +} +``` + +## Event optimization + +### 1. Use indexed parameters wisely + +```rust +sol! { + // ✅ Index frequently-queried fields (max 3 indexed) + event Transfer( + address indexed from, + address indexed to, + uint256 value // Not indexed - saves gas + ); + + // ❌ Bad: Too many indexed parameters + event TooManyIndexed( + address indexed from, + address indexed to, + uint256 indexed amount, // Expensive to index + uint256 indexed timestamp // 4th indexed param - not allowed! + ); +} +``` + +**Gas impact**: Each indexed parameter costs ~375 additional gas. Only index fields you'll search by. + +### 2. Batch events when possible + +```rust +// ✅ Emit single event for batch operation +sol! { + event BatchTransfer( + address indexed from, + address[] to, + uint256[] amounts + ); +} + +pub fn batch_transfer( + &mut self, + recipients: Vec
, + amounts: Vec +) -> Result<(), Vec> { + // Process transfers... + + // Single event instead of N events + evm::log(BatchTransfer { + from: msg::sender(), + to: recipients, + amounts, + }); + + Ok(()) +} +``` + +## Binary size optimization + +Smaller WASM binaries cost less to deploy and activate. + +### 1. Optimize compilation flags + +```toml +# Cargo.toml +[profile.release] +opt-level = "z" # Optimize for size +lto = true # Link-time optimization +codegen-units = 1 # Better optimization +strip = true # Remove debug symbols +panic = "abort" # Smaller panic handling +``` + +### 2. Avoid large dependencies + +```rust +// ❌ Bad: Heavy dependency for simple task +use fancy_math_library::complex_sqrt; // Adds 50KB to binary + +pub fn calculate(&self, value: U256) -> U256 { + complex_sqrt(value) // Using 1% of library +} + +// ✅ Good: Implement simple operations yourself +pub fn simple_sqrt(&self, value: U256) -> U256 { + // Custom implementation adds minimal binary size + // Newton's method or similar +} +``` + +### 3. Use cargo stylus optimization + +```shell +# Check binary size +cargo stylus check + +# Optimize with wasm-opt +cargo stylus deploy --optimize + +# Maximum optimization (slower build, smaller binary) +cargo stylus deploy --optimize-level 3 +``` + +## Gas measurement + +### 1. Profile your contracts + +```rust +#[cfg(test)] +mod gas_tests { + use super::*; + + #[test] + fn benchmark_transfer() { + let vm = TestVM::default(); + let mut contract = Token::from(&vm); + + // Measure gas for operation + let gas_before = vm.gas_left(); + contract.transfer(recipient, amount).unwrap(); + let gas_used = gas_before - vm.gas_left(); + + println!("Transfer gas used: {}", gas_used); + assert!(gas_used < 50000, "Transfer too expensive"); + } +} +``` + +### 2. Compare implementations + +```rust +#[cfg(test)] +mod optimization_tests { + #[test] + fn compare_storage_patterns() { + // Test pattern A + let gas_a = measure_pattern_a(); + + // Test pattern B + let gas_b = measure_pattern_b(); + + println!("Pattern A: {} gas", gas_a); + println!("Pattern B: {} gas", gas_b); + println!("Savings: {}%", (gas_a - gas_b) * 100 / gas_a); + } +} +``` + +## Optimization checklist + +Before deploying, verify you've: + +- [ ] Minimized storage reads and writes +- [ ] Cached frequently-accessed storage values +- [ ] Used appropriate data types +- [ ] Deleted unused storage for gas refunds +- [ ] Avoided unnecessary clones and allocations +- [ ] Optimized hot code paths +- [ ] Minimized cross-contract calls +- [ ] Used indexed events sparingly +- [ ] Optimized WASM binary size +- [ ] Profiled gas usage for critical functions +- [ ] Compared against Solidity baseline (if porting) + +## Common optimizations summary + +| Pattern | Gas Savings | Complexity | +| ------------------------- | ------------------------------ | ---------- | +| Cache storage reads | High (100+ gas per read saved) | Low | +| Delete unused storage | Medium (15,000 gas refund) | Low | +| Batch storage writes | Medium (varies) | Medium | +| Use iterators vs. collect | Low-Medium | Low | +| Minimize external calls | High | Medium | +| Optimize binary size | High (deployment only) | Medium | +| Right-size data types | Low-Medium | Low | + +## Advanced optimization + +### Custom memory allocators + +For advanced users, custom allocators can reduce memory overhead: + +```rust +#[global_allocator] +static ALLOCATOR: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; +``` + +**Warning**: Only use if you understand the trade-offs. + +### Assembly optimization + +For critical paths, you can use WASM intrinsics: + +```rust +use core::arch::wasm32::*; + +// ✅ Advanced: Use WASM intrinsics for critical operations +pub fn optimized_hash(&self, data: &[u8]) -> [u8; 32] { + // WASM-optimized hashing + // Only use if you're an advanced developer +} +``` + +## Next steps + +- Apply [security best practices](/stylus/best-practices/security) +- Study [deployment optimization](/stylus/guides/optimizing-binaries) +- Learn about [caching strategies](/stylus/guides/caching-contracts) +- Review [debugging techniques](/stylus/cli-tools/debugging-tx) diff --git a/docs/stylus/best-practices/security.mdx b/docs/stylus/best-practices/security.mdx new file mode 100644 index 0000000000..a3e320835a --- /dev/null +++ b/docs/stylus/best-practices/security.mdx @@ -0,0 +1,499 @@ +--- +title: 'Security best practices' +sidebar_label: 'Security' +description: 'Essential security patterns and guidelines for writing secure Stylus smart contracts' +user_story: 'As a developer, I want to write secure Stylus contracts that protect user funds and prevent exploits' +content_type: concept +author: offchainlabs +sme: offchainlabs +sidebar_position: 1 +--- + +Writing secure smart contracts is critical - vulnerabilities can lead to loss of funds and user trust. This guide covers essential security patterns for Stylus development. + +## Core security principles + +### 1. Input validation + +Always validate external inputs before using them in your contract logic. + +```rust +use stylus_sdk::{prelude::*, msg}; +use alloy_primitives::{Address, U256}; + +#[external] +impl MyContract { + // ❌ Bad: No validation + pub fn transfer_bad(&mut self, recipient: Address, amount: U256) -> Result<(), Vec> { + let sender = msg::sender(); + self.balances.setter(sender).sub_assign(amount); + self.balances.setter(recipient).add_assign(amount); + Ok(()) + } + + // ✅ Good: Proper validation + pub fn transfer_good(&mut self, recipient: Address, amount: U256) -> Result<(), Vec> { + // Validate inputs + ensure!(!recipient.is_zero(), "Invalid recipient"); + ensure!(amount > U256::ZERO, "Amount must be positive"); + + let sender = msg::sender(); + let sender_balance = self.balances.get(sender); + + // Check sufficient balance + ensure!(sender_balance >= amount, "Insufficient balance"); + + // Safe arithmetic + self.balances.setter(sender).sub_assign(amount); + self.balances.setter(recipient).add_assign(amount); + + Ok(()) + } +} +``` + +### 2. Access control + +Implement proper authorization checks for privileged operations. + +```rust +use stylus_sdk::{prelude::*, msg, storage::StorageAddress}; + +sol_storage! { + pub struct Ownable { + StorageAddress owner; + } +} + +#[external] +impl Ownable { + // Initialize owner in constructor-like pattern + pub fn init(&mut self) -> Result<(), Vec> { + let owner = self.owner.get(); + ensure!(owner.is_zero(), "Already initialized"); + self.owner.set(msg::sender()); + Ok(()) + } + + // Modifier pattern for owner-only functions + fn only_owner(&self) -> Result<(), Vec> { + ensure!(msg::sender() == self.owner.get(), "Not authorized"); + Ok(()) + } + + pub fn sensitive_operation(&mut self) -> Result<(), Vec> { + self.only_owner()?; + // Perform privileged operation + Ok(()) + } + + pub fn transfer_ownership(&mut self, new_owner: Address) -> Result<(), Vec> { + self.only_owner()?; + ensure!(!new_owner.is_zero(), "Invalid new owner"); + self.owner.set(new_owner); + Ok(()) + } +} +``` + +### 3. Reentrancy protection + +Protect against reentrancy attacks using the checks-effects-interactions pattern. + +```rust +use stylus_sdk::{prelude::*, call::transfer_eth, msg}; + +sol_storage! { + pub struct Vault { + StorageMap balances; + StorageBool locked; // Reentrancy guard + } +} + +#[external] +impl Vault { + // ❌ Bad: Vulnerable to reentrancy + pub fn withdraw_bad(&mut self, amount: U256) -> Result<(), Vec> { + let sender = msg::sender(); + let balance = self.balances.get(sender); + + ensure!(balance >= amount, "Insufficient balance"); + + // DANGER: External call before state update + transfer_eth(sender, amount)?; + + // State updated after external call - vulnerable! + self.balances.setter(sender).sub_assign(amount); + Ok(()) + } + + // ✅ Good: Checks-Effects-Interactions pattern + pub fn withdraw_good(&mut self, amount: U256) -> Result<(), Vec> { + // Check: Reentrancy guard + ensure!(!self.locked.get(), "Reentrancy detected"); + self.locked.set(true); + + let sender = msg::sender(); + let balance = self.balances.get(sender); + + // Check: Validate conditions + ensure!(balance >= amount, "Insufficient balance"); + + // Effect: Update state BEFORE external call + self.balances.setter(sender).sub_assign(amount); + + // Interaction: External call last + let result = transfer_eth(sender, amount); + + // Release lock + self.locked.set(false); + + result + } +} +``` + +### 4. Safe arithmetic + +While Rust prevents overflows in debug mode, use explicit checks for production. + +```rust +use alloy_primitives::U256; + +#[external] +impl SafeMath { + // ✅ Use checked arithmetic + pub fn safe_add(&self, a: U256, b: U256) -> Result> { + a.checked_add(b).ok_or("Arithmetic overflow".into()) + } + + pub fn safe_mul(&self, a: U256, b: U256) -> Result> { + a.checked_mul(b).ok_or("Arithmetic overflow".into()) + } + + // ✅ Validate before operations + pub fn calculate_fee(&self, amount: U256, basis_points: U256) -> Result> { + ensure!(basis_points <= U256::from(10000), "Invalid fee"); + + amount + .checked_mul(basis_points) + .and_then(|v| v.checked_div(U256::from(10000))) + .ok_or("Fee calculation failed".into()) + } +} +``` + +## Common vulnerabilities + +### Integer overflow/underflow + +**Risk**: Arithmetic operations that exceed type limits can cause unexpected behavior. + +**Prevention**: + +```rust +// ✅ Use checked operations +let result = value.checked_add(amount).ok_or("Overflow")?; + +// ✅ Or use saturating operations when appropriate +let capped_value = value.saturating_add(amount); +``` + +### Unchecked external calls + +**Risk**: Failed external calls may be silently ignored. + +**Prevention**: + +```rust +// ❌ Bad: Ignoring call result +let _ = external_contract.call(data); + +// ✅ Good: Handle all results +external_contract.call(data).map_err(|e| "External call failed")?; +``` + +### Front-running + +**Risk**: Transactions visible in mempool can be exploited by miners or bots. + +**Prevention**: + +```rust +// ✅ Use commit-reveal pattern for sensitive operations +sol_storage! { + pub struct CommitReveal { + StorageMap commits; + StorageMap reveal_times; + } +} + +impl CommitReveal { + pub fn commit(&mut self, commitment: [u8; 32]) -> Result<(), Vec> { + let sender = msg::sender(); + self.commits.setter(sender).set(commitment); + self.reveal_times.setter(sender).set(block::timestamp() + 100); + Ok(()) + } + + pub fn reveal(&mut self, value: U256, salt: [u8; 32]) -> Result<(), Vec> { + let sender = msg::sender(); + + // Verify commit period passed + ensure!( + block::timestamp() >= self.reveal_times.get(sender), + "Too early to reveal" + ); + + // Verify commitment + let expected = keccak256(&[&value.to_be_bytes(), &salt].concat()); + ensure!( + expected == self.commits.get(sender), + "Invalid reveal" + ); + + // Process reveal... + Ok(()) + } +} +``` + +### Denial of Service (DoS) + +**Risk**: Unbounded loops or operations that can be griefed. + +**Prevention**: + +```rust +// ❌ Bad: Unbounded loop +pub fn distribute_rewards_bad(&mut self, recipients: Vec
) -> Result<(), Vec> { + for recipient in recipients { + // Could run out of gas with too many recipients + self.send_reward(recipient)?; + } + Ok(()) +} + +// ✅ Good: Paginated or pull-based pattern +pub fn distribute_rewards_good( + &mut self, + start_index: U256, + count: U256 +) -> Result<(), Vec> { + ensure!(count <= U256::from(50), "Batch too large"); + + let end = start_index + count; + for i in start_index..end { + let recipient = self.recipients.get(i); + if !recipient.is_zero() { + self.send_reward(recipient)?; + } + } + Ok(()) +} + +// ✅ Better: Pull-based (users claim their own rewards) +pub fn claim_reward(&mut self) -> Result<(), Vec> { + let sender = msg::sender(); + let reward = self.pending_rewards.get(sender); + + ensure!(reward > U256::ZERO, "No rewards"); + + self.pending_rewards.setter(sender).set(U256::ZERO); + transfer_eth(sender, reward)?; + + Ok(()) +} +``` + +## Storage security + +### Visibility and access patterns + +```rust +sol_storage! { + pub struct SecureVault { + // Public read, controlled write + StorageU256 public_total; + + // Private storage - not visible off-chain without knowing slot + StorageMap private_balances; + + // Owner-controlled + StorageAddress owner; + } +} + +#[external] +impl SecureVault { + // ✅ Expose only what's necessary + pub fn get_total(&self) -> U256 { + self.public_total.get() + } + + // ✅ Don't expose internal mappings directly + pub fn get_balance(&self, account: Address) -> Result> { + ensure!(msg::sender() == account || msg::sender() == self.owner.get(), "Unauthorized"); + Ok(self.private_balances.get(account)) + } +} +``` + +### Prevent storage collisions + +```rust +// ✅ Use unique storage namespace for upgradeable contracts +sol_storage! { + pub struct MyContract { + // Prefix with contract name to avoid collisions + #[borrow] + MyContractStorage storage; + } +} + +sol_storage! { + pub struct MyContractStorage { + StorageU256 value; + StorageMap balances; + } +} +``` + +## Error handling + +### Informative error messages + +```rust +#[derive(SolidityError)] +pub enum MyError { + InsufficientBalance(U256), + Unauthorized(Address), + InvalidAmount(U256), + TransferFailed(Address, U256), +} + +#[external] +impl MyContract { + pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), MyError> { + let sender = msg::sender(); + let balance = self.balances.get(sender); + + if balance < amount { + return Err(MyError::InsufficientBalance(balance)); + } + + if to.is_zero() { + return Err(MyError::InvalidAmount(amount)); + } + + // Transfer logic... + Ok(()) + } +} +``` + +### Fail securely + +```rust +// ✅ Fail closed, not open +pub fn privileged_function(&mut self) -> Result<(), Vec> { + // Default to denying access + let is_authorized = self.check_authorization(msg::sender()); + + // Explicit check required to proceed + ensure!(is_authorized, "Access denied"); + + // Privileged operation + Ok(()) +} +``` + +## Testing for security + +### Write comprehensive tests + +```rust +#[cfg(test)] +mod tests { + use super::*; + use stylus_sdk::testing::*; + + #[test] + fn test_reentrancy_protection() { + let vm = TestVM::default(); + let mut contract = Vault::from(&vm); + + // Setup + contract.deposit(U256::from(100)).unwrap(); + + // Attempt reentrancy + let result = contract.withdraw(U256::from(50)); + assert!(result.is_ok()); + + // Second withdrawal should fail if still locked + let result2 = contract.withdraw(U256::from(50)); + assert!(result2.is_err()); + } + + #[test] + fn test_access_control() { + let vm = TestVM::default(); + let mut contract = Ownable::from(&vm); + + // Initialize owner + contract.init().unwrap(); + + // Non-owner should be rejected + vm.set_caller(address!("0x0000000000000000000000000000000000000001")); + assert!(contract.sensitive_operation().is_err()); + + // Owner should succeed + vm.set_caller(/* original owner */); + assert!(contract.sensitive_operation().is_ok()); + } + + #[test] + fn test_arithmetic_safety() { + let vm = TestVM::default(); + let contract = SafeMath::from(&vm); + + // Test overflow + let max = U256::MAX; + let result = contract.safe_add(max, U256::from(1)); + assert!(result.is_err()); + + // Test valid operation + let result = contract.safe_add(U256::from(1), U256::from(2)); + assert_eq!(result.unwrap(), U256::from(3)); + } +} +``` + +## Security checklist + +Before deploying your contract, verify: + +- [ ] All external inputs are validated +- [ ] Access control is implemented for privileged functions +- [ ] Reentrancy guards protect state-changing functions +- [ ] Arithmetic operations use checked methods +- [ ] External call results are handled +- [ ] Error messages don't leak sensitive information +- [ ] Storage visibility is appropriate +- [ ] No unbounded loops or arrays +- [ ] Critical functions have comprehensive tests +- [ ] Code has been reviewed by another developer +- [ ] Consider professional audit for high-value contracts + +## Additional resources + +- [Stylus Security Audit](https://docs.arbitrum.io/stylus/reference/audit-reports) +- [Rust Security Guidelines](https://anssi-fr.github.io/rust-guide/) +- [Smart Contract Security Best Practices](https://consensys.github.io/smart-contract-best-practices/) +- [OWASP Smart Contract Top 10](https://owasp.org/www-project-smart-contract-top-10/) + +## Next steps + +- Review [gas optimization best practices](/stylus/best-practices/gas-optimization) +- Study [error handling patterns](/stylus/fundamentals/contracts#error-handling) +- Explore [testing strategies](/stylus/fundamentals/testing-contracts) diff --git a/docs/stylus/cli-tools-overview.md b/docs/stylus/cli-tools-overview.md deleted file mode 100644 index ba9675b99d..0000000000 --- a/docs/stylus/cli-tools-overview.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: cli-tools-overview -title: CLI Tools (cargo-stylus) -sidebar_label: CLI tools overview ---- - -The CLI tools provided for Stylus, specifically the `cargo-stylus` tool, are designed to help developers manage, compile, and optimize their Stylus contracts efficiently. This overview provides a summary of the tools available and how to use them effectively. - -## Available tools - -### 1. Optimize WASM binaries - -The `cargo-stylus` tool allows you to optimize WebAssembly (WASM) binaries, ensuring that your contracts are as efficient as possible. - -- **[Optimize WASM binaries](/stylus/how-tos/optimizing-binaries.mdx):** Learn how to optimize your WASM binaries for performance and size. - -### 2. Debug Stylus transactions - -Gain insights into your Stylus contracts by debugging transactions. - -- **[Debug Stylus transactions](/stylus/how-tos/debugging-tx.mdx):** A guide to debugging transactions, helping you identify and fix issues. - -### 3. Verify contracts - -Ensure that your Stylus contracts are correctly verified. - -- **[Verify contracts](/stylus/how-tos/verifying-contracts.mdx):** Step-by-step instructions on how to verify your contracts using `cargo-stylus`. - -## Source code repository - -The source code for `cargo-stylus` is available on GitHub. Explore the code, contribute, or use it as a reference. - -- **[cargo-stylus repository](https://github.com/OffchainLabs/stylus):** Visit the GitHub repository for more information. - -## Additional resources - -For more advanced usage and detailed guides, refer to the following resources: - -- **[Optimize WASM binaries](/stylus/how-tos/optimizing-binaries.mdx)** -- **[Troubleshooting](/stylus/troubleshooting-building-stylus.md)** -- **[Run a Stylus dev node](/run-arbitrum-node/03-run-local-full-chain-simulation.mdx)** - -This overview page serves as the starting point for mastering the CLI tools available for Stylus development. From optimizing your contracts to debugging and verifying them, the `cargo-stylus` toolset is integral to a smooth development experience. diff --git a/docs/stylus/cli-tools/_category_.yml b/docs/stylus/cli-tools/_category_.yml new file mode 100644 index 0000000000..5a13bee5be --- /dev/null +++ b/docs/stylus/cli-tools/_category_.yml @@ -0,0 +1,5 @@ +label: 'CLI tools' +position: 4 +link: + type: generated-index + description: cargo-stylus command-line tools for building, deploying, and verifying contracts. diff --git a/docs/stylus/cli-tools/check-and-deploy.mdx b/docs/stylus/cli-tools/check-and-deploy.mdx new file mode 100644 index 0000000000..b4c419ba60 --- /dev/null +++ b/docs/stylus/cli-tools/check-and-deploy.mdx @@ -0,0 +1,779 @@ +--- +title: 'Check and deploy' +description: 'Check and deploy Stylus contracts' +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers who need to understand how to check and deploy Stylus contracts. +displayed_sidebar: buildStylusSidebar +--- + +This guide explains how to validate and deploy Stylus smart contracts using the `cargo stylus` CLI tool. The process involves two main steps: checking that your contract is valid, and deploying it to an Arbitrum chain. + +## Prerequisites + +Before checking or deploying contracts, ensure you have: + +1. **Rust toolchain** installed (see [rustup.rs](https://rustup.rs)) +2. **WebAssembly target** added: + ```shell + rustup target add wasm32-unknown-unknown + ``` +3. **cargo-stylus CLI** installed: + ```shell + cargo install cargo-stylus + ``` +4. **RPC endpoint** for the target chain (see [testnet information](https://docs.arbitrum.io/stylus/reference/testnet-information)) +5. **Funded account** with ETH for gas and activation fees + +## Overview: The Two-Step Process + +Deploying a Stylus contract involves two distinct steps: + +1. **Deployment**: Upload the compressed WASM bytecode to the chain, assigning it an address +2. **Activation**: Trigger onchain compilation to native code and cache it for fast execution + +The `cargo stylus check` command validates your contract before deployment, and `cargo stylus deploy` handles both steps automatically. + +## Checking Contracts + +The `cargo stylus check` command validates that your contract can be deployed and activated without actually sending a transaction. + +### What check does + +1. **Compiles** your Rust code to WASM with `wasm32-unknown-unknown` target +2. **Compresses** the WASM using brotli compression +3. **Validates** the WASM structure: + - Required exports (`user_entrypoint`) + - Allowed imports (only `vm_hooks`) + - Memory constraints + - Size limits (24KB compressed) +4. **Simulates activation** using `eth_call` to verify onchain compatibility +5. **Estimates data fee** required for activation + +### Basic usage + +```shell +# Check the current project against Arbitrum Sepolia (default) +cargo stylus check +``` + +### Common options + +```shell +# Check against a specific network +cargo stylus check \ + --endpoint="https://arb1.arbitrum.io/rpc" + +# Check a specific WASM file +cargo stylus check \ + --wasm-file=./target/wasm32-unknown-unknown/release/my_contract.wasm + +# Check with a specific contract address +cargo stylus check \ + --contract-address=0x1234567890123456789012345678901234567890 +``` + +### Success output + +When your contract passes validation: + +```shell +Finished release [optimized] target(s) in 1.88s +Reading WASM file at target/wasm32-unknown-unknown/release/my_contract.wasm +Compressed WASM size: 3 KB +Contract succeeded Stylus onchain activation checks with Stylus version: 1 +wasm data fee: 0.0001 ETH (originally 0.00008 ETH with 20% bump) +``` + +### Failure output + +If validation fails, you'll see detailed error information: + +```shell +Reading WASM file at target/wasm32-unknown-unknown/release/bad_contract.wasm +Compressed WASM size: 55 B +Stylus checks failed: contract predeployment check failed + +Caused by: + binary exports reserved symbol stylus_ink_left + +Location: + prover/src/binary.rs:493:9 +``` + +Common validation errors include: + +- **Missing entrypoint**: Contract lacks `#[entrypoint]` attribute +- **Invalid exports**: Contract exports reserved symbols +- **Size limit exceeded**: Compressed WASM exceeds 24KB +- **Invalid imports**: Contract imports functions outside `vm_hooks` +- **Memory violations**: Incorrect memory handling or growth + +## Deploying Contracts + +The `cargo stylus deploy` command compiles, deploys, and activates your contract in a single operation. + +### What deploy does + +1. **Compiles and checks** the contract (same as `cargo stylus check`) +2. **Deploys bytecode**: Sends transaction to upload compressed WASM to the chain +3. **Activates contract**: Calls `activateProgram` precompile to compile to native code +4. **Verifies success**: Confirms both transactions completed successfully + +### Basic deployment + +```shell +# Deploy to Arbitrum Sepolia (default testnet) +cargo stylus deploy \ + --private-key-path=./key.txt +``` + +### Deployment with gas estimation + +Before deploying, estimate the gas required: + +```shell +cargo stylus deploy \ + --private-key-path=./key.txt \ + --estimate-gas +``` + +Output: + +```shell +Compressed WASM size: 3 KB +Deploying contract to address 0x457b1ba688e9854bdbed2f473f7510c476a3da09 +Estimated gas: 12756792 +wasm data fee: 0.0001 ETH +``` + +### Full deployment + +Once estimation looks correct, deploy for real: + +```shell +cargo stylus deploy \ + --private-key-path=./key.txt +``` + +Output shows both transactions: + +```shell +Compressed WASM size: 3 KB +Deploying contract to address 0x457b1ba688e9854bdbed2f473f7510c476a3da09 +Estimated gas: 12756792 +Submitting tx... +Confirmed tx 0x42db...7311, gas used 11657164 + +Activating contract at address 0x457b1ba688e9854bdbed2f473f7510c476a3da09 +Estimated gas: 14251759 +Submitting tx... +Confirmed tx 0x0bdb...3307, gas used 14204908 +``` + +### Deployment options + +#### Network selection + +```shell +# Deploy to Arbitrum One (mainnet) +cargo stylus deploy \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --private-key-path=./key.txt + +# Deploy to Arbitrum Sepolia (testnet) +cargo stylus deploy \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \ + --private-key-path=./key.txt +``` + +#### Private key management + +```shell +# From file (recommended) +cargo stylus deploy \ + --private-key-path=./key.txt + +# From environment (WARNING: exposes key to shell history) +cargo stylus deploy \ + --private-key=$PRIVATE_KEY + +# From keystore file +cargo stylus deploy \ + --keystore-path=./keystore.json \ + --keystore-password-path=./password.txt +``` + +#### Gas price control + +```shell +# Set custom gas price (in gwei) +cargo stylus deploy \ + --private-key-path=./key.txt \ + --max-fee-per-gas-gwei=0.05 +``` + +#### Deploy without activation + +For advanced use cases, deploy bytecode without immediate activation: + +```shell +cargo stylus deploy \ + --private-key-path=./key.txt \ + --no-activate +``` + +Later, activate separately: + +```shell +cargo stylus activate \ + --address=0x457b1ba688e9854bdbed2f473f7510c476a3da09 \ + --private-key-path=./key.txt +``` + +#### Constructor arguments + +Deploy contracts with constructor arguments: + +```shell +cargo stylus deploy \ + --private-key-path=./key.txt \ + --constructor-args "Hello" "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" 42 +``` + +Send ETH to payable constructor: + +```shell +cargo stylus deploy \ + --private-key-path=./key.txt \ + --constructor-value=0.1 \ + --constructor-args "InitialValue" +``` + +#### Reproducible builds + +For contract verification, use Docker-based reproducible builds: + +```shell +# Default: uses Docker for reproducibility +cargo stylus deploy \ + --private-key-path=./key.txt + +# Specify cargo-stylus version +cargo stylus deploy \ + --private-key-path=./key.txt \ + --cargo-stylus-version=0.5.0 +``` + +Skip Docker for local development (non-reproducible): + +```shell +cargo stylus deploy \ + --private-key-path=./key.txt \ + --no-verify +``` + +## Understanding Activation + +Activation is the process of compiling WASM to native machine code onchain. + +### Why activation is required + +- **Performance**: Native code executes 10-100x faster than interpreted WASM +- **Validation**: Ensures WASM is well-formed and follows all constraints +- **Caching**: Compiled code is cached for all future contract calls + +### Activation process + +1. **Decompress**: Brotli-compressed WASM is decompressed +2. **Validate**: WASM structure is checked for correctness +3. **Compile**: WASM is compiled to native machine code +4. **Cache**: Compiled code is stored in the activation cache +5. **Charge fee**: Data fee based on WASM size is charged + +### Data fee calculation + +The data fee depends on the size of your WASM: + +```rust +// From activation.rs +pub async fn data_fee( + code: impl Into, + address: Address, + config: &ActivationConfig, + provider: &impl Provider, +) -> Result { + let result = arbwasm + .activateProgram(address) + .call() + .await?; + + let data_fee = result.dataFee; + let bump = config.data_fee_bump_percent; // Default 20% + let adjusted = bump_data_fee(data_fee, bump); + + Ok(adjusted) +} +``` + +By default, the fee is bumped by 20% to account for gas price fluctuations. + +### Activation errors + +Common activation failures: + +#### Missing entrypoint + +``` +Error: Contract could not be activated as it is missing an entrypoint. +Please ensure that your contract has an #[entrypoint] defined on your main struct +``` + +**Solution**: Add `#[entrypoint]` to your main storage struct: + +```rust +#[entrypoint] +#[storage] +pub struct MyContract { + // ... +} +``` + +#### Insufficient funds + +``` +Error: not enough funds in account 0x... to pay for data fee +balance 0.0001 ETH < 0.0005 ETH +``` + +**Solution**: Fund your account with more ETH. Get testnet ETH from faucets: + +- [Arbitrum Sepolia faucet](https://faucet.quicknode.com/arbitrum/sepolia) + +#### Invalid WASM + +``` +Error: contract activation failed: failed to parse contract +Caused by: binary exports reserved symbol stylus_ink_left +``` + +**Solution**: Ensure you're using the latest `stylus-sdk` version and following SDK conventions. + +## Deployment Workflows + +### Development workflow + +For rapid iteration during development: + +```shell +# 1. Check frequently during development +cargo stylus check + +# 2. Deploy to testnet with no-verify for speed +cargo stylus deploy \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \ + --private-key-path=./key.txt \ + --no-verify + +# 3. Test the deployed contract +# (use your testing framework) + +# 4. Iterate and redeploy as needed +``` + +### Production workflow + +For production deployments: + +```shell +# 1. Final check against mainnet +cargo stylus check \ + --endpoint="https://arb1.arbitrum.io/rpc" + +# 2. Estimate costs +cargo stylus deploy \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --private-key-path=./key.txt \ + --estimate-gas + +# 3. Deploy with reproducible build (for verification) +cargo stylus deploy \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --private-key-path=./key.txt \ + --cargo-stylus-version=0.5.0 + +# 4. Verify the deployed contract +cargo stylus verify \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --deployment-tx=0x... +``` + +### Multi-contract deployment + +Deploy multiple contracts from a workspace: + +```shell +# Check all contracts in workspace +cargo stylus check + +# Deploy specific contract +cargo stylus deploy \ + --contract=my-token \ + --private-key-path=./key.txt + +# Deploy all contracts (must have no-arg constructors) +cargo stylus deploy \ + --private-key-path=./key.txt +``` + +## Checking Existing Deployments + +### Check if contract is activated + +```rust +// From check.rs +let codehash = processed.codehash(); +if Contract::exists(codehash, &provider).await? { + return Ok(ContractStatus::Active { + code: processed.code, + }); +} +``` + +Use `cargo stylus check` with `--contract-address` to verify an existing deployment: + +```shell +cargo stylus check \ + --contract-address=0x457b1ba688e9854bdbed2f473f7510c476a3da09 +``` + +### Re-activation + +If a contract is already deployed but not activated, activate it: + +```shell +cargo stylus activate \ + --address=0x457b1ba688e9854bdbed2f473f7510c476a3da09 \ + --private-key-path=./key.txt +``` + +## Best Practices + +### 1. Always check before deploying + +```shell +# ✅ Good: Check first +cargo stylus check +cargo stylus deploy --private-key-path=./key.txt + +# ❌ Bad: Deploy without checking +cargo stylus deploy --private-key-path=./key.txt +``` + +### 2. Use gas estimation + +```shell +# ✅ Good: Estimate first +cargo stylus deploy --private-key-path=./key.txt --estimate-gas +# Review the output, then deploy for real +cargo stylus deploy --private-key-path=./key.txt + +# ❌ Bad: Deploy without estimation +cargo stylus deploy --private-key-path=./key.txt +``` + +### 3. Secure private key handling + +```shell +# ✅ Good: Use key file +echo $PRIVATE_KEY > /tmp/key.txt +chmod 600 /tmp/key.txt +cargo stylus deploy --private-key-path=/tmp/key.txt +rm /tmp/key.txt + +# ⚠️ Risky: Expose key in command line +cargo stylus deploy --private-key=$PRIVATE_KEY +``` + +### 4. Test on testnet first + +```shell +# ✅ Good: Test on Sepolia first +cargo stylus deploy \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \ + --private-key-path=./key.txt + +# After testing succeeds, deploy to mainnet +cargo stylus deploy \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --private-key-path=./key.txt + +# ❌ Bad: Deploy directly to mainnet +cargo stylus deploy \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --private-key-path=./key.txt +``` + +### 5. Use reproducible builds for verification + +```shell +# ✅ Good: Reproducible build for mainnet +cargo stylus deploy \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --private-key-path=./key.txt \ + --cargo-stylus-version=0.5.0 + +# Then verify on Arbiscan +cargo stylus verify \ + --endpoint="https://arb1.arbitrum.io/rpc" \ + --deployment-tx=0x... + +# ⚠️ OK for development: Skip Docker +cargo stylus deploy \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \ + --private-key-path=./key.txt \ + --no-verify +``` + +### 6. Monitor contract size + +```shell +# Check compressed size +cargo stylus check + +# If size is close to 24KB limit: +# - Use #[no_std] +# - Remove unused dependencies +# - Enable aggressive optimizations +# - Strip debug symbols +``` + +### 7. Verify data fee is reasonable + +```shell +# Check data fee before deploying +cargo stylus deploy --private-key-path=./key.txt --estimate-gas + +# Output shows: +# wasm data fee: 0.0001 ETH (originally 0.00008 ETH with 20% bump) + +# If fee seems high: +# - Optimize WASM size +# - Check network congestion +# - Verify contract correctness +``` + +## Troubleshooting + +### Size limit errors + +**Error**: Compressed WASM exceeds 24KB + +**Solutions**: + +1. Use `#[no_std]` to eliminate standard library: + + ```rust + #![no_std] + extern crate alloc; + ``` + +2. Remove unused dependencies from `Cargo.toml`: + + ```toml + [dependencies] + stylus-sdk = "0.5" + # Remove unnecessary crates + ``` + +3. Enable size optimizations in `Cargo.toml`: + + ```toml + [profile.release] + opt-level = "z" + lto = true + strip = true + ``` + +4. Use `wasm-opt` for additional optimization: + ```shell + wasm-opt -Oz -o optimized.wasm input.wasm + cargo stylus deploy --wasm-file=optimized.wasm --private-key-path=./key.txt + ``` + +### Activation failures + +**Error**: Transaction reverted during activation + +**Solutions**: + +1. Verify entrypoint exists: + + ```rust + #[entrypoint] + #[storage] + pub struct MyContract { /* ... */ } + ``` + +2. Check WASM validity: + + ```shell + cargo stylus check --wasm-file=./target/wasm32-unknown-unknown/release/my_contract.wasm + ``` + +3. Ensure sufficient funds: + + ```shell + # Check balance + cast balance $YOUR_ADDRESS --rpc-url $RPC_URL + + # Get testnet ETH if needed + # Visit faucet.quicknode.com/arbitrum/sepolia + ``` + +### RPC errors + +**Error**: Connection timeout or RPC error + +**Solutions**: + +1. Verify endpoint URL: + + ```shell + curl -X POST $RPC_URL \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' + ``` + +2. Try alternative endpoints: + + ```shell + # Arbitrum One + --endpoint="https://arb1.arbitrum.io/rpc" + --endpoint="https://arbitrum-one.publicnode.com" + + # Arbitrum Sepolia + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" + --endpoint="https://arbitrum-sepolia.blockpi.network/v1/rpc/public" + ``` + +3. Check network status: + - [Arbitrum Status](https://arbiscan.io/) + - [Chainlist](https://chainlist.org/) + +### Build errors + +**Error**: Compilation fails + +**Solutions**: + +1. Update dependencies: + + ```shell + cargo update + ``` + +2. Clear build cache: + + ```shell + cargo clean + ``` + +3. Verify Rust toolchain: + + ```shell + rustup update + rustup target add wasm32-unknown-unknown + ``` + +4. Check SDK version compatibility: + ```toml + [dependencies] + stylus-sdk = "0.5" # Use latest stable version + ``` + +## Non-Rust WASM Deployment + +Deploy WASM from any language (C, C++, etc.): + +```shell +# Deploy raw WASM file +cargo stylus deploy \ + --wasm-file=./my_contract.wasm \ + --private-key-path=./key.txt + +# Deploy WebAssembly Text (WAT) file +cargo stylus deploy \ + --wasm-file=./my_contract.wat \ + --private-key-path=./key.txt +``` + +Example WAT file structure: + +```wasm +(module + (memory 0 0) + (export "memory" (memory 0)) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + )) +``` + +## Command Reference + +### cargo stylus check + +**Syntax**: + +```shell +cargo stylus check [OPTIONS] +``` + +**Common options**: + +- `--endpoint=`: RPC endpoint (default: Arbitrum Sepolia) +- `--wasm-file=`: Check specific WASM file +- `--contract-address=
`: Target contract address + +### cargo stylus deploy + +**Syntax**: + +```shell +cargo stylus deploy [OPTIONS] +``` + +**Common options**: + +- `--endpoint=`: RPC endpoint +- `--private-key-path=`: Private key file +- `--estimate-gas`: Only estimate gas +- `--no-activate`: Deploy without activation +- `--no-verify`: Skip Docker reproducible build +- `--constructor-args `: Constructor arguments +- `--constructor-value=`: ETH sent to constructor +- `--max-fee-per-gas-gwei=`: Custom gas price + +### cargo stylus activate + +**Syntax**: + +```shell +cargo stylus activate --address=
[OPTIONS] +``` + +**Options**: + +- `--address=
`: Deployed contract address (required) +- `--private-key-path=`: Private key file +- `--estimate-gas`: Only estimate gas + +## Resources + +- [Stylus quickstart guide](https://docs.arbitrum.io/stylus/stylus-quickstart) +- [Cargo Stylus repository](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) +- [Testnet information](https://docs.arbitrum.io/stylus/reference/testnet-information) +- [Contract verification guide](https://docs.arbitrum.io/stylus/guides/verifying-contracts) +- [Optimizing WASM size](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md) +- [Valid WASM requirements](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus/blob/main/VALID_WASM.md) diff --git a/docs/stylus/cli-tools/commands-reference.mdx b/docs/stylus/cli-tools/commands-reference.mdx new file mode 100644 index 0000000000..7f4539c50c --- /dev/null +++ b/docs/stylus/cli-tools/commands-reference.mdx @@ -0,0 +1,258 @@ +--- +title: 'cargo-stylus command reference' +sidebar_label: 'Commands reference' +description: 'Complete reference for all cargo-stylus CLI commands' +user_story: 'As a Stylus developer, I want a complete reference of all CLI commands' +content_type: reference +author: offchainlabs +sme: offchainlabs +sidebar_position: 5 +--- + +Complete reference for all `cargo-stylus` commands. + +## new + +Create a new Stylus project. + +**Usage:** + +```shell +cargo stylus new +``` + +**Options:** + +- `--minimal` - Create minimal project structure + +**Example:** + +```shell +cargo stylus new my-stylus-contract +``` + +## init + +Initialize a Stylus project in the current directory. + +**Usage:** + +```shell +cargo stylus init +``` + +**Options:** + +- `--minimal` - Create minimal project structure + +## check + +Verify a contract compiles to valid WASM and passes onchain activation checks. + +**Usage:** + +```shell +cargo stylus check +``` + +**Options:** + +- `--endpoint ` - RPC endpoint (default: http://localhost:8547) +- `--wasm-file ` - WASM file to check (defaults to any found in current directory) +- `--contract-address
` - Optional deployment address + +**Example:** + +```shell +cargo stylus check --endpoint https://sepolia-rollup.arbitrum.io/rpc +``` + +## deploy + +Deploy a Stylus contract. + +**Usage:** + +```shell +cargo stylus deploy \ + --endpoint \ + --private-key +``` + +**Options:** + +- `--endpoint ` - RPC endpoint (required) +- `--private-key ` - Private key for deployment (required) +- `--contract-address
` - Specific deployment address (defaults to random) +- `--estimate-gas` - Estimate gas only, don't deploy +- `--no-verify` - Skip reproducible container verification +- `--wasm-file ` - WASM file to deploy +- `--max-fee-per-gas-gwei ` - Optional max fee per gas + +**Example:** + +```shell +cargo stylus deploy \ + --endpoint https://sepolia-rollup.arbitrum.io/rpc \ + --private-key $PRIVATE_KEY \ + --estimate-gas +``` + +## activate + +Activate an already deployed contract. + +**Usage:** + +```shell +cargo stylus activate \ + --endpoint \ + --private-key \ + --address +``` + +**Options:** + +- `--endpoint ` - RPC endpoint (required) +- `--private-key ` - Private key for activation (required) +- `--address
` - Contract address to activate (required) +- `--data-fee-bump-percent ` - Percent to bump estimated fee (default: 20%) +- `--estimate-gas` - Only estimate gas without sending transaction + +## verify + +Verify the deployment of a Stylus contract. + +**Usage:** + +```shell +cargo stylus verify \ + --deployment-tx +``` + +**Options:** + +- `--endpoint ` - RPC endpoint +- `--deployment-tx ` - Deployment transaction hash (required) +- `--no-verify` - Skip reproducible container +- `--cargo-stylus-version ` - Version for Docker image + +**Example:** + +```shell +cargo stylus verify --deployment-tx 0xd4...85 +``` + +## cache + +Manage contract caching using the Stylus CacheManager. + +**Usage:** + +```shell +# Place a bid on a contract +cargo stylus cache bid --address
+ +# Check contract cache status +cargo stylus cache status --address
+ +# Get suggested minimum bid +cargo stylus cache suggest-bid --address
+``` + +**Options:** + +- `--address
` - Contract address (required) +- `--endpoint ` - RPC endpoint + +## export-abi + +Export a Solidity ABI interface. + +**Usage:** + +```shell +cargo stylus export-abi +``` + +**Options:** + +- `--output ` - Output file (defaults to stdout) +- `--json` - Write JSON ABI using solc format + +**Example:** + +```shell +cargo stylus export-abi > IMyContract.sol +cargo stylus export-abi --json > abi.json +``` + +## cgen + +Generate C code bindings for a Stylus contract. + +**Usage:** + +```shell +cargo stylus cgen \ + --input \ + --out-dir +``` + +**Options:** + +- `--input ` - Input file path (required) +- `--out-dir ` - Output directory path (required) + +## replay + +Replay a transaction in GDB for debugging. + +**Usage:** + +```shell +cargo stylus replay --tx +``` + +**Options:** + +- `--tx ` - Transaction hash to replay (required) +- `--endpoint ` - RPC endpoint +- `--project ` - Project path (default: current directory) +- `--use-native-tracer` - Use native tracer instead of JavaScript +- `--stable-rust` - Use stable Rust (note: nightly needed to expand macros) + +## trace + +Trace a transaction. + +**Usage:** + +```shell +cargo stylus trace --tx +``` + +**Options:** + +- `--tx ` - Transaction hash (required) +- `--endpoint ` - RPC endpoint +- `--project ` - Project path +- `--use-native-tracer` - Use native tracer + +--- + +## Common options + +These options are available across multiple commands: + +| Option | Description | +| ------------------------ | --------------------------------------------- | +| `--endpoint ` | RPC endpoint URL | +| `--private-key ` | Private key for transactions | +| `--estimate-gas` | Estimate gas without sending transaction | +| `--no-verify` | Skip reproducible container verification | +| `--cargo-stylus-version` | Specify cargo-stylus version for Docker image | + +--- + +For detailed usage examples, see the [CLI tools guides](/stylus/cli-tools/overview). diff --git a/docs/stylus/how-tos/debugging-tx.mdx b/docs/stylus/cli-tools/debugging-tx.mdx similarity index 96% rename from docs/stylus/how-tos/debugging-tx.mdx rename to docs/stylus/cli-tools/debugging-tx.mdx index c47cb69116..aac7157564 100644 --- a/docs/stylus/how-tos/debugging-tx.mdx +++ b/docs/stylus/cli-tools/debugging-tx.mdx @@ -7,14 +7,14 @@ sme: mahsamoosavi target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 2 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- Debugging smart contracts can be challenging, especially when dealing with complex transactions. The `cargo-stylus` crate simplifies the debugging process by allowing developers to replay Stylus transactions. This tool leverages GDB to provide an interactive debugging experience, enabling developers to set breakpoints, inspect state changes, and trace the execution flow step-by-step. This capability is crucial for identifying and resolving issues, ensuring that smart contracts function correctly and efficiently. ### Overview -Cargo Stylus is a tool designed to simplify the development and debugging process for smart contracts written in Rust for the Stylus execution environment. One of its powerful features is the `cargo stylus` subcommand, which provides essential functionalities for developers: +Cargo Stylus simplifies the development and debugging of Rust smart contracts for the Stylus execution environment. One of its powerful features is the `cargo stylus` subcommand, which provides essential functionalities for developers: 1. **Trace transactions**: Perform trace calls against Stylus transactions using Ethereum nodes' `debug_traceTransaction` RPC. This feature enables developers to analyze the execution flow and state changes of their transactions in a detailed manner. 2. **Debugging with GDB or LLDB**: Replay and debug the execution of a Stylus transaction using a debugger. This allows developers to set breakpoints, inspect variables, and step through the transaction execution line by line, providing an in-depth understanding of the transaction's behavior. diff --git a/docs/stylus/cli-tools/overview.mdx b/docs/stylus/cli-tools/overview.mdx new file mode 100644 index 0000000000..bcd8207d4b --- /dev/null +++ b/docs/stylus/cli-tools/overview.mdx @@ -0,0 +1,131 @@ +--- +id: 'overview' +title: 'Using Stylus CLI' +description: 'Get started with Stylus CLI, a Rust toolkit for developing Stylus contracts' +author: 'anegg0' +sme: 'anegg0' +sidebar_position: 2 +target_audience: Developers writing Stylus contracts in Rust using Stylus +displayed_sidebar: buildStylusSidebar +--- + +This guide will get you started using [cargo stylus](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus), a CLI toolkit to help developers manage, compile, deploy, and optimize their Stylus contracts efficiently. + +This overview will help you discover and learn how to uses cargo stylus tools. + +### Installing cargo stylus + +Cargo stylus is a plugin to the standard cargo tool for developing Rust programs. + +#### Prerequisites + +
+Rust toolchain + +Follow the instructions on [Rust Lang's installation page](https://www.rust-lang.org/tools/install) to install a complete Rust toolchain (v1.81 or newer) on your system. After installation, ensure you can access the programs `rustup`, `rustc`, and `cargo` from your preferred terminal application. + +
+ +
+Docker + +We will use the testnet, and some `cargo stylus` commands will require Docker to operate. + +You can download Docker from [Docker's website](https://www.docker.com/products/docker-desktop). + +
+ +
+Foundry's Cast + +[Foundry's Cast](https://book.getfoundry.sh/cast/) is a command-line tool for interacting with your EVM contracts. + +
+ +
+Nitro devnode + +Stylus is available on Arbitrum Sepolia, but we'll use Nitro devnode, which has a pre-funded wallet, saving us the effort of wallet provisioning or running out of tokens to send transactions. + +```shell title="Install your devnode" +git clone https://github.com/OffchainLabs/nitro-devnode.git +cd nitro-devnode +``` + +```shell title="Launch your devnode" +./run-dev-node.sh +``` + +
+ +#### Installation + +In your terminal, run: + +```shell +cargo install --force cargo-stylus +``` + +Add WASM ([WebAssembly](https://webassembly.org/)) as a build target for the specific Rust toolchain you are using. The below example sets your default Rust toolchain to 1.80 as well as adding the WASM build target: + +```shell +rustup default 1.80 +rustup target add wasm32-unknown-unknown --toolchain 1.80 +``` + +You can verify the cargo stylus installation by running `cargo stylus -V` in your terminal, returning something like:`stylus 0.5.6` + +### Using cargo stylus + +#### Cargo Stylus Commands Reference + +| Command | Description | Arguments | Options | Example Usage | +| ------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `new` | Create a new Stylus project | • `name`: Project name (required) | • `--minimal`: Create a minimal contract | `cargo stylus new ` | +| `init` | Initialize a Stylus project in current directory | | • `--minimal`: Create a minimal contract | `cargo stylus init --minimal` | +| `export-abi` | Export a Solidity ABI | | • `--output`: Output file (defaults to stdout)
• `--json`: Write JSON ABI using `solc` | `cargo stylus export-abi --json` | +| `activate` | Activate an already deployed contract | • `--address`: Contract address to activate | • `--data-fee-bump-percent`: Percent to bump estimated fee (default 20%)
• `--estimate-gas`: Only estimate gas without sending transaction | `cargo stylus activate --address ` | +| `cache` | Cache contract using Stylus CacheManager | • `bid`: Place bid on contract
• `status`: Check contract status
• `suggest-bid`: Get suggested minimum bid | | `cargo stylus cache bid --address ` | +| `check` | Check a contract | | • `--wasm-file`: WASM file to check
• `--contract-address`: Deployment address | | +| `deploy` | Deploy a contract | • `--contract-address `: Where to deploy and activate the contract (defaults to a random address) | • `--estimate-gas`: Only perform estimation
• `--no-verify`: Skip reproducible container
• `--cargo-stylus-version`: Version for Docker image
• `--source-files-for-project-hash `: Path to source files to include in the project hash
• `--max-fee-per-gas-gwei `: Optional max fee per gas in `gwei` units
• `--wasm-file `: The WASM file to check (defaults to any found in the current directory) | `cargo stylus deploy --endpoint='http://localhost:8547' --private-key="" --estimate-gas` | +| `verify` | Verify contract deployment | • `--deployment-tx`: Hash of deployment transaction | • `--no-verify`: Skip reproducible container
• `--cargo-stylus-version`: Version for Docker image | | +| `cgen` | Generate C code bindings | • `--input`: Input file path
• `--out_dir`: Output directory path | | | +| `replay` | Replay transaction in GDB | • `-t, --tx `: Transaction to replay | • `-p, --project `: Project path (default: `.`)
• `-u, --use-native-tracer`: Use the native tracer instead of the JavaScript one (may not be available in the node)
• `-s, --stable-rust`: Use stable Rust (note that nightly is needed to expand macros) | `cargo stylus replay --tx ` | +| `trace` | Trace a transaction | • `--tx`: Transaction hash | • `--endpoint`: RPC endpoint
• `--project`: Project path
• `--use-native-tracer`: Use native tracer | | + +##### Common options + +These options are available across multiple commands: + +| Option | Description | +| ------------------------------- | ------------------------------------------------------ | +| --endpoint | Arbitrum RPC endpoint (default: http://localhost:8547) | +| --verbose | Print debug info | +| --source-files-for-project-hash | Paths to source files for project hash | +| --max-fee-per-gas-gwei | Optional max fee per gas in `gwei` | + +##### Authentication options + +Available for commands involving transactions: + +| Option | Description | +| ------------------------ | ---------------------------------------------------- | +| --private-key-path | Path to file containing hex-encoded private key | +| --private-key | Private key as hex string (exposes to shell history) | +| --keystore-path | Path to Ethereum wallet keystore file | +| --keystore-password-path | Keystore password file path | + +#### How-tos + +| Topic | Description | +| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| [Learn how to optimize WASM binaries](/stylus/guides/optimizing-binaries) | The `cargo-stylus` tool allows you to optimize WebAssembly (WASM) binaries, ensuring that your contracts are as efficient as possible. | +| [Debug Stylus transactions](/stylus/cli-tools/debugging-tx) | A guide to debugging transactions, helping you identify and fix issues. Gain insights into your Stylus contracts by debugging transactions. | +| [Verify contracts](/stylus/cli-tools/verify-contracts) | Ensure that your Stylus contracts are correctly verified. Step-by-step instructions on how to verify your contracts using `cargo-stylus`. | +| [Run a Stylus dev node](/run-arbitrum-node/run-local-full-chain-simulation) | Learn how to run a local Arbitrum dev node to test your Stylus contracts. | + +#### Additional resources + +#### [Troubleshooting](/stylus/troubleshooting-building-stylus): solve the most common issues. + +#### [cargo-stylus repository](https://github.com/OffchainLabs/cargo-stylus): consult cargo stylus' source code. diff --git a/docs/stylus/cli-tools/verify-contracts.mdx b/docs/stylus/cli-tools/verify-contracts.mdx new file mode 100644 index 0000000000..fafd7d2f85 --- /dev/null +++ b/docs/stylus/cli-tools/verify-contracts.mdx @@ -0,0 +1,270 @@ +--- +title: 'How to verify Stylus contracts' +sidebar_label: 'Verify contracts' +description: 'Learn how to verify Stylus contracts locally and on Arbiscan' +user_story: 'As a Stylus developer, I want to verify my contracts to ensure reproducibility and transparency' +content_type: how-to +author: rauljordan, mahsamoosavi +sme: rauljordan, mahsamoosavi +target_audience: 'Developers deploying smart contracts using Stylus' +sidebar_position: 2 +displayed_sidebar: buildStylusSidebar +--- + +import ImageZoom from '@site/src/components/ImageZoom'; + +Stylus contracts can be verified in two ways: locally using `cargo stylus`, or on [Arbiscan](https://arbiscan.io/), Arbitrum's block explorer. This guide covers both methods. + +:::info +Stylus contract verification on Arbiscan is only supported for contracts deployed using `cargo-stylus` 0.5.0 or higher. +::: + +## Overview: Why verify contracts? + +Contract verification ensures that: + +- Your deployment is reproducible by anyone running the same environment +- Users can independently verify that deployed bytecode matches published source code +- The contract source code is publicly available on block explorers +- Trust and transparency are established with your users + +## Local verification + +Local verification uses the `cargo stylus` tool to verify contracts against your local codebase. + +### Goals + +- Ensure Stylus contract deployments are reproducible by anyone on the same architecture +- Sandbox the reproducible environment and standardize it +- Guarantee that programs reproducibly deployed with cargo stylus version ≥ 0.4.2 are verifiable + +### Opting out + +By default, `cargo stylus deploy` is reproducible as it runs in a Docker container. You can opt out by specifying `--no-verify`: + +```shell +cargo stylus deploy --no-verify +``` + +### How it works + +When you deploy a contract, the deployment transaction's `data` field contains your compressed contract code. The `cargo stylus` tool compresses and encodes your contract's WASM in a standardized, reproducible way. + +Anyone with the same codebase can rebuild your contract and verify the output matches the onchain data. This is similar to [Solidity contract verification](https://docs.sourcify.dev/docs/how-to-verify/#how-does-verification-work) but for WASM-based contracts. + +### Verification flow + +``` +cargo stylus deploy + ↓ +Compressed WASM bytecode sent to chain + ↓ +cargo stylus verify + ↓ +Rebuilds WASM and compares with onchain code + ↓ +Reports: ✓ Verified or ✗ Mismatch +``` + +### Prerequisites for local verification + +1. Docker installed and running +2. The exact same codebase used for deployment +3. `cargo-stylus` CLI installed (version ≥ 0.4.2) + +### Running local verification + +Use the `cargo stylus verify` command with your deployment transaction hash: + +```shell +cargo stylus verify --deployment-tx 0xd4...85 +``` + +**Example output (successful verification):** + +``` +Reading deployment tx from chain... +Wasm data hash: 0xf80a... +Program succeeded Stylus onchain activation checks. +Connecting to Docker... +Reproducing wasm binary from local code... (This may take a while) +Finished release build in X.XXs + +INFO: contract code matches the onchain code ✓ +``` + +**Example output (mismatch):** + +``` +Reading deployment tx from chain... +ERROR: contract code does not match the onchain code ✗ +``` + +### Troubleshooting local verification + +**Error: Docker not running** + +``` +Error: Cannot connect to Docker daemon +``` + +Solution: Start Docker Desktop or Docker daemon + +**Error: Mismatch** + +If verification fails, common causes: + +- Different Rust toolchain version +- Different dependency versions +- Modified source code +- Different build flags + +Ensure you're using the exact same codebase and Rust version as the deployment. + +--- + +## Arbiscan verification + +Arbiscan provides a web-based interface for contract verification, making your source code publicly visible on the block explorer. + +### View verified contracts + +You can browse verified Stylus contracts on: + +- [Verified Stylus Contracts on Arbitrum One](https://arbiscan.io/contractsVerified?filter=stylus) +- [Verified Stylus Contracts on Arbitrum Sepolia](https://sepolia.arbiscan.io/contractsVerified?filter=stylus) + +Example: [English Auction Stylus contract](https://sepolia.arbiscan.io/address/0xe85a046fd3ea22ceeb3caef3a0d38123eecbe3ca) (verified on Arbitrum Sepolia) + +### Step 1: Navigate to the verification page + +You have two options: + +**Option A: Direct link** +Visit [Arbiscan Verify Contract](https://arbiscan.io/verifyContract) directly if you have the contract address ready. + +**Option B: From the contract page** + +1. Go to your contract's page on Arbiscan +2. Click the "Contract" tab +3. Click the "Verify and Publish" link + + + +### Step 2: Enter contract details + +On the verification page, provide: + +1. **Contract address**: The deployed contract address (e.g., `0xe85a...3ca`) +2. **Compiler type**: Select **"Stylus (Rust)"** from the dropdown + + + +3. **cargo-stylus version**: Select the version you used for deployment (must be ≥ 0.5.0) + + + +Then click **Continue**. + +### Step 3: Submit source code + +You can submit your source code in two ways: + +#### Option A: Single Rust file + +If your contract is a single `.rs` file: + +1. Click **"Rust File Upload"** +2. Upload your `.rs` file + +#### Option B: Standard JSON input + +For multi-file projects: + +1. Click **"Standard JSON Input"** +2. Generate the JSON using: + +```shell +cargo stylus verify --json +``` + +This generates a `project.json` file containing all sources and metadata. + +3. Upload the `project.json` file + + + +Click **Verify and Publish** to complete verification. + +### Step 4: Verification result + +If successful, you'll see: + +- ✅ Verification successful message +- Your source code is now publicly visible on Arbiscan +- The "Contract" tab shows your Rust source code + + + +### Handling previously verified contracts + +If your contract was already verified: + +1. Arbiscan will detect this automatically +2. It will display: "Contract Source Code Already Verified" +3. You can view the existing verification in the Contract tab + + + +### Troubleshooting Arbiscan verification + +**Error: cargo-stylus version too old** + +Arbiscan requires cargo-stylus ≥ 0.5.0. Update your toolchain: + +```shell +cargo install cargo-stylus --force +``` + +**Error: Verification failed** + +Common causes: + +- Wrong cargo-stylus version selected +- Source code doesn't match deployed bytecode +- Missing dependencies in JSON file + +Solution: Ensure you're using the exact source code from deployment and the correct cargo-stylus version. + +**Error: Contract not found** + +Ensure: + +- The contract address is correct +- The contract is deployed on the selected network (Arbitrum One vs Sepolia) +- The deployment transaction is confirmed + +--- + +## Which verification method should I use? + +| Method | When to use | Benefits | +| ------------------------- | --------------------------------------- | --------------------------------------------------------------------- | +| **Local verification** | Quick verification during development | Fast, no external dependencies, proves reproducibility | +| **Arbiscan verification** | Publishing verified contracts for users | Public source code visibility, block explorer integration, user trust | + +**Best practice**: Use both methods: + +1. Verify locally after deployment to ensure reproducibility +2. Verify on Arbiscan to publish source code for your users + +--- + +## Next steps + +- [Learn about deployment](./check-and-deploy.mdx) +- [Explore CLI tools](./overview.mdx) +- [View verified examples](https://github.com/OffchainLabs/stylus-by-example) diff --git a/docs/stylus/concepts/activation.mdx b/docs/stylus/concepts/activation.mdx new file mode 100644 index 0000000000..812565fdf0 --- /dev/null +++ b/docs/stylus/concepts/activation.mdx @@ -0,0 +1,778 @@ +--- +title: 'Activation' +description: 'Understanding Stylus contract deployment and activation' +user_story: 'As a developer, I want to understand the two-step deployment and activation process' +content_type: concept +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers deploying Stylus contracts to Arbitrum chains. +displayed_sidebar: buildStylusSidebar +--- + +Stylus contracts undergo a two-step process to become executable on Arbitrum chains: **deployment** and **activation**. This guide explains both steps, the distinction between them, and how to manage the activation process. + +## Overview + +Unlike traditional EVM contracts that become immediately executable after deployment, Stylus contracts require an additional activation step: + +1. **Deployment**: Stores the compressed WASM bytecode onchain at a contract address +2. **Activation**: Converts the bytecode into an executable Stylus program by registering it with the ArbWasm precompile + +**Why two steps?** + +- **Gas optimization**: Activation involves one-time processing and caching that would be expensive to repeat on every call +- **Code reuse**: Multiple contracts can share the same activated codehash, reducing activation costs +- **Version management**: Allows the chain to track which Stylus protocol version a contract targets + +## Deployment vs Activation + +| Aspect | Deployment | Activation | +| --------------------- | ----------------------------- | ------------------------------ | +| **Purpose** | Store compressed WASM onchain | Register program with ArbWasm | +| **Transaction count** | 1 transaction | 1 transaction (separate) | +| **Cost type** | Standard EVM deployment gas | Data fee (WASM-specific cost) | +| **When required** | Always - stores the code | Always - makes code executable | +| **Reversible** | No | No (but can expire) | +| **Who can call** | Anyone with funds | Anyone (after deployment) | +| **Can be skipped** | No | No (unless already activated) | + +### Contract States + +A Stylus contract can be in one of these states: + +```rust +pub enum ContractStatus { + /// Contract already exists onchain and is activated + Active { code: Vec }, + + /// Contract is deployed but not yet activated + /// Ready to activate with the given data fee + Ready { code: Vec, fee: U256 }, +} +``` + +## The Activation Process + +### Step 1: Build and Process WASM + +Before deployment, your Rust contract is compiled and processed: + +```bash +cargo stylus check +``` + +This performs: + +1. **Compile Rust to WASM**: Using `wasm32-unknown-unknown` target +2. **Process WASM binary**: + - Remove dangling references + - Add project hash metadata + - Strip unnecessary custom sections +3. **Brotli compression**: Maximum compression (level 11) +4. **Add EOF prefix**: `EFF00000` (identifies Stylus programs) +5. **Size validation**: Compressed code must be ≤ 24KB + +**WASM Processing Pipeline**: + +![Activation Process](/img/stylus-activation-process.png) + +_Figure 1: WASM binary processing pipeline showing transformation from raw binary to deployment-ready compressed code._ + +### Step 2: Deploy the Contract + +Deployment creates a transaction that stores your processed WASM onchain: + +```bash +cargo stylus deploy \ + --private-key-path=key.txt \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" +``` + +**What happens during deployment:** + +1. **Generate deployment bytecode**: Create EVM initcode with embedded compressed WASM +2. **Estimate gas**: Calculate deployment transaction gas cost +3. **Send deployment transaction**: To Stylus deployer contract +4. **Extract contract address**: From transaction receipt + +**Deployment Bytecode Structure**: + +``` +EVM Initcode Prelude (43 bytes): +┌─────────────────────────────────────┐ +│ 0x7f PUSH32 │ Push code length +│ 0x80 DUP1 │ Duplicate length +│ 0x60 PUSH1 │ Push prelude length +│ 0x60 PUSH1 0x00 │ Push 0 +│ 0x39 CODECOPY │ Copy code to memory +│ 0x60 PUSH1 0x00 │ Push 0 +│ 0xf3 RETURN │ Return code +│ 0x00 │ Stylus version +└─────────────────────────────────────┘ + ↓ + +``` + +### Step 3: Calculate Activation Fee + +Before activating, the data fee must be calculated: + +```rust +// Simulated via state overrides (no transaction sent) +let data_fee = calculate_activation_fee(contract_address); + +// Apply bump percentage for safety (default: 20%) +let final_fee = data_fee * (1 + bump_percent / 100); +``` + +**Data fee calculation**: + +- Uses state override simulation to estimate fee +- No actual transaction sent during estimation +- Configurable bump percentage protects against variance (default: 20%) +- Fee is paid in ETH when activating + +### Step 4: Activate the Contract + +Activation registers your contract with the ArbWasm precompile: + +```bash +# Automatic activation (default) +cargo stylus deploy --private-key-path=key.txt + +# Or manual activation +cargo stylus activate \ + --address=0x1234... \ + --private-key-path=key.txt +``` + +**What happens during activation:** + +1. **Call ArbWasm precompile**: At address `0x0000000000000000000000000000000000000071` +2. **Send activation transaction**: + ```solidity + ArbWasm.activateProgram{value: dataFee}(contractAddress) + ``` +3. **ArbWasm processes the code**: + - Validates WASM format + - Checks against protocol version + - Stores activation metadata + - Emits `ProgramActivated` event +4. **Returns activation info**: + ```solidity + returns (uint16 version, uint256 actualDataFee) + ``` + +## Using cargo-stylus + +The `cargo-stylus` CLI tool simplifies the deployment and activation workflow. + +### Basic Deployment (Automatic Activation) + +By default, `cargo stylus deploy` handles both steps: + +```bash +cargo stylus deploy \ + --private-key-path=wallet.txt \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" +``` + +**Output**: + +``` +Building contract... +Compressing WASM... +Deploying contract to 0x1234567890abcdef... +Deployment transaction: 0xabcd... +Contract deployed at: 0x1234567890abcdef +Activating contract... +Activation transaction: 0xef12... +Contract activated successfully! +``` + +### Deploy Without Activation + +To deploy but skip activation: + +```bash +cargo stylus deploy \ + --private-key-path=wallet.txt \ + --no-activate +``` + +This is useful when: + +- You want to inspect the contract before activating +- Someone else will handle activation +- You're testing deployment workflows + +### Manual Activation + +Activate a previously deployed contract: + +```bash +cargo stylus activate \ + --address=0x1234567890abcdef \ + --private-key-path=wallet.txt \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" +``` + +### Check Contract Status + +Before deploying, check if a contract with the same code is already activated: + +```bash +cargo stylus check \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" +``` + +**Possible outcomes**: + +1. **Code already activated**: You can reuse the existing deployment +2. **Ready to activate**: Shows estimated data fee +3. **Validation errors**: Displays issues that must be fixed + +## Deployment with Constructors + +If your contract has a constructor, provide arguments during deployment: + +```rust +#[public] +impl MyContract { + #[constructor] + pub fn constructor(&mut self, initial_value: U256, owner: Address) { + self.value.set(initial_value); + self.owner.set(owner); + } +} +``` + +**Deploy with constructor arguments**: + +```bash +cargo stylus deploy \ + --private-key-path=wallet.txt \ + --constructor-args 42 0x1234567890abcdef1234567890abcdef12345678 +``` + +**With payable constructor**: + +```rust +#[constructor] +#[payable] +pub fn constructor(&mut self) { + let value = self.vm().msg_value(); + self.initial_balance.set(value); +} +``` + +```bash +cargo stylus deploy \ + --private-key-path=wallet.txt \ + --constructor-value=1000000000000000000 # 1 ETH in wei +``` + +## The ArbWasm Precompile + +Activation is handled by the ArbWasm precompile at address `0x0000000000000000000000000000000000000071`. + +### Key Functions + +#### activateProgram + +Activates a deployed Stylus contract: + +```solidity +function activateProgram( + address program +) external payable returns (uint16 version, uint256 dataFee); +``` + +**Parameters**: + +- `program`: Contract address containing WASM bytecode + +**Payment**: + +- Must send `value` equal to the calculated data fee (in wei) + +**Returns**: + +- `version`: Stylus protocol version the program was activated against +- `dataFee`: Actual fee paid for activation + +**Example (via cast)**: + +```bash +cast send 0x0000000000000000000000000000000000000071 \ + "activateProgram(address)" \ + 0x1234567890abcdef \ + --value 100000000000000000 \ + --private-key=$PRIVATE_KEY +``` + +#### codehashVersion + +Check if a codehash is activated and get its version: + +```solidity +function codehashVersion(bytes32 codehash) external view returns (uint16 version); +``` + +**Reverts if**: + +- Code is not activated +- Program needs upgrade +- Program has expired + +#### programTimeLeft + +Get remaining time before a program expires: + +```solidity +function programTimeLeft(address program) external view returns (uint64 timeLeft); +``` + +Returns seconds until expiration (default: ~1 year from activation). + +#### codehashKeepalive + +Extend a program's expiration time: + +```solidity +function codehashKeepalive(bytes32 codehash) external payable returns (uint64 expirySeconds); +``` + +Resets the expiration timer, preventing program deactivation. + +### ArbWasm Errors + +Activation can fail with these errors: + +```solidity +error ProgramNotWasm(); +// The deployed bytecode is not valid WASM + +error ProgramNotActivated(); +// Contract exists but hasn't been activated + +error ProgramNeedsUpgrade(uint16 version, uint16 stylusVersion); +// Program version incompatible with current Stylus version + +error ProgramExpired(uint64 ageInSeconds); +// Program has expired and must be reactivated + +error ProgramInsufficientValue(uint256 have, uint256 want); +// Sent data fee is less than required +``` + +## Gas and Fee Optimization + +### Estimating Costs + +Get cost estimates before deploying: + +```bash +# Estimate deployment gas +cargo stylus deploy --estimate-gas + +# Check activation fee +cargo stylus check # Shows estimated data fee +``` + +### Fee Bump Configuration + +Protect against fee variance with configurable bump percentage: + +```bash +# Default: 20% bump +cargo stylus deploy --private-key-path=wallet.txt + +# Custom bump percentage +# (Note: Use programmatically via stylus-tools library) +``` + +**In code** (using stylus-tools): + +```rust +use stylus_tools::core::activation::ActivationConfig; + +let config = ActivationConfig { + data_fee_bump_percent: 25, // 25% safety margin +}; +``` + +### Code Reuse Optimization + +If your contract's codehash matches an already-activated contract: + +```bash +cargo stylus check +``` + +**Output if already activated**: + +``` +Checking contract... +✓ Contract with this codehash is already activated! +Version: 1 +No activation needed - you can deploy without activating. +``` + +You can deploy the contract normally, and it will automatically use the existing activation. + +### Contract Caching + +After activation, contracts can be cached for cheaper calls: + +```solidity +// ArbWasmCache precompile (0x0000000000000000000000000000000000000072) +function cacheProgram(address program) external payable returns (uint256); +``` + +**Benefits**: + +- Reduces gas costs for subsequent contract calls +- One-time caching fee +- Shared across all contracts with same codehash + +## Advanced Activation Patterns + +### Multi-Contract Deployment + +When deploying multiple instances of the same contract: + +```bash +# First deployment: full deploy + activate +cargo stylus deploy --private-key-path=wallet.txt +# Contract 1: 0xaaaa... (activated) + +# Subsequent deployments: deploy only (reuses activation) +cargo stylus deploy --private-key-path=wallet.txt --no-activate +# Contract 2: 0xbbbb... (uses existing activation) + +cargo stylus deploy --private-key-path=wallet.txt --no-activate +# Contract 3: 0xcccc... (uses existing activation) +``` + +All three contracts share the same codehash and activation, saving on data fees. + +### Programmatic Deployment + +Using the stylus-tools library directly: + +```rust +use stylus_tools::core::{ + deployment::{deploy, DeploymentConfig}, + activation::{activate_contract, ActivationConfig, data_fee}, + check::{check_contract, ContractStatus}, +}; +use alloy::providers::{Provider, WalletProvider}; + +async fn deploy_and_activate( + provider: &impl Provider + WalletProvider, +) -> Result> { + let contract = /* build contract */; + + // Step 1: Check if already activated + let config = CheckConfig::default(); + match check_contract(&contract, None, &config, provider).await? { + ContractStatus::Active { .. } => { + println!("Already activated!"); + // Deploy without activation + } + ContractStatus::Ready { code, fee } => { + println!("Ready to activate. Data fee: {}", fee); + // Continue with deployment + activation + } + } + + // Step 2: Deploy + let deploy_config = DeploymentConfig { + no_activate: false, + ..Default::default() + }; + + deploy(&contract, &deploy_config, provider).await?; + + // Contract address returned from deployment + Ok(contract_address) +} +``` + +### Custom Deployer Contracts + +Use a custom deployer contract instead of the default: + +```bash +cargo stylus deploy \ + --private-key-path=wallet.txt \ + --deployer-address=0x... \ + --deployer-salt=0x0000000000000000000000000000000000000000000000000000000000000001 +``` + +This is useful for: + +- CREATE2 deterministic addresses +- Custom deployment logic +- Factory patterns + +## Contract Lifecycle + +### Activation Lifecycle + +``` +Deployed → Activated → [Active] → [Keepalive] → [Expired] + ↑ ↓ + └──────────┘ + (Periodic keepalive) +``` + +### Expiration and Keepalive + +Programs automatically expire after ~1 year (configurable by chain): + +```solidity +// Check time remaining +uint64 timeLeft = ArbWasm.programTimeLeft(contractAddress); + +// Extend expiration +ArbWasm.codehashKeepalive{value: keepaliveFee}(codehash); +``` + +**Why expiration?** + +- Prevents abandoned contracts from consuming ArbOS resources +- Encourages active maintenance +- Allows protocol upgrades + +**Keepalive strategy**: + +- Monitor `programTimeLeft()` periodically +- Call `codehashKeepalive()` before expiration +- Automated scripts can handle this + +### Reactivation After Expiry + +If a program expires: + +```bash +# Reactivate the existing deployment +cargo stylus activate --address=0x... +``` + +The contract code remains onchain; only the activation state was cleared. + +## Troubleshooting + +### Common Activation Errors + +#### "Program not activated" + +**Cause**: Trying to call a deployed but not activated contract + +**Solution**: + +```bash +cargo stylus activate --address=0x... +``` + +#### "Insufficient value" + +**Cause**: Data fee sent is less than required + +**Solution**: + +- Check current data fee: `cargo stylus check` +- Increase fee bump percentage +- Ensure sufficient ETH balance + +#### "Program not WASM" + +**Cause**: Deployed bytecode is not valid Stylus WASM + +**Solution**: + +- Verify you deployed the correct contract +- Rebuild and redeploy: `cargo stylus deploy` + +#### "Program needs upgrade" + +**Cause**: Contract was activated against an old Stylus version + +**Solution**: + +- Recompile with latest SDK +- Redeploy and reactivate + +#### "Program expired" + +**Cause**: Contract hasn't been kept alive and expired + +**Solution**: + +```bash +# Reactivate the contract +cargo stylus activate --address=0x... +``` + +### Debugging Activation + +Enable verbose output: + +```bash +# Check detailed status +cargo stylus check --verbose + +# Deploy with verbose logging +RUST_LOG=debug cargo stylus deploy --private-key-path=wallet.txt +``` + +### Verifying Activation Status + +Check if a contract is activated: + +```bash +# Via cargo-stylus +cargo stylus check --address=0x... + +# Via cast (calling ArbWasm) +cast call 0x0000000000000000000000000000000000000071 \ + "codehashVersion(bytes32)" \ + $(cast keccak $(cast code 0x...)) +``` + +## Best Practices + +### 1. Always Check Before Deploying + +```bash +cargo stylus check +``` + +This prevents deploying duplicate code and wasting gas. + +### 2. Use Automatic Activation + +Unless you have specific reasons to split deployment and activation, use the default behavior: + +```bash +cargo stylus deploy # Deploys AND activates +``` + +### 3. Test on Testnet First + +Deploy to Arbitrum Sepolia before mainnet: + +```bash +cargo stylus deploy \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \ + --private-key-path=testnet-key.txt +``` + +### 4. Monitor Contract Expiration + +Set up monitoring for production contracts: + +```solidity +uint64 timeLeft = ArbWasm.programTimeLeft(contractAddress); +if (timeLeft < 30 days) { + // Send alert or trigger keepalive +} +``` + +### 5. Document Activation Details + +Track activation information: + +- Contract address +- Activation transaction hash +- Stylus version +- Data fee paid +- Activation timestamp + +### 6. Keep SDK Updated + +Use the latest Stylus SDK version: + +```toml +[dependencies] +stylus-sdk = "0.10.0-beta.1" +``` + +Older versions may become incompatible with chain upgrades. + +### 7. Handle Constructor Arguments Carefully + +Type-check constructor arguments: + +```bash +# Incorrect (will fail) +cargo stylus deploy --constructor-args "hello" 123 + +# Correct (matches constructor signature) +cargo stylus deploy --constructor-args 0x1234... 42 +``` + +## Complete Example + +Here's a full deployment workflow: + +```bash +# 1. Create new Stylus project +cargo stylus new my-token +cd my-token + +# 2. Build and verify locally +cargo build --release --target wasm32-unknown-unknown +cargo stylus check + +# 3. Test on Arbitrum Sepolia +export SEPOLIA_ENDPOINT="https://sepolia-rollup.arbitrum.io/rpc" +export PRIVATE_KEY_PATH="./sepolia-key.txt" + +cargo stylus deploy \ + --endpoint=$SEPOLIA_ENDPOINT \ + --private-key-path=$PRIVATE_KEY_PATH \ + --constructor-args "MyToken" "MTK" 18 + +# 4. Verify deployment +cargo stylus check \ + --endpoint=$SEPOLIA_ENDPOINT \ + --address=0x... # Address from step 3 + +# 5. Deploy to mainnet +export MAINNET_ENDPOINT="https://arb1.arbitrum.io/rpc" +export MAINNET_KEY_PATH="./mainnet-key.txt" + +cargo stylus deploy \ + --endpoint=$MAINNET_ENDPOINT \ + --private-key-path=$MAINNET_KEY_PATH \ + --constructor-args "MyToken" "MTK" 18 + +# 6. Cache the contract (optional, for gas optimization) +cast send 0x0000000000000000000000000000000000000072 \ + "cacheProgram(address)" \ + 0x... # Your contract address \ + --value 10000000000000000 \ + --rpc-url=$MAINNET_ENDPOINT \ + --private-key=$MAINNET_PRIVATE_KEY +``` + +## Summary + +- **Two-step process**: Deployment stores code, activation makes it executable +- **cargo-stylus handles both**: Use `deploy` for automatic activation +- **Data fee required**: Activation costs ETH (separate from deployment gas) +- **Code reuse**: Identical contracts share activation, saving costs +- **Expiration**: Programs expire after ~1 year without keepalive +- **ArbWasm precompile**: All activation goes through address `0x71` +- **Check first**: Use `cargo stylus check` to avoid duplicate activations + +## See Also + +- [Contracts](../fundamentals/contracts.mdx) - Writing Stylus contracts +- [Global Variables and Functions](../fundamentals/global-variables-and-functions.mdx) - VM interface methods + + diff --git a/docs/stylus/concepts/gas-metering.mdx b/docs/stylus/concepts/gas-metering.mdx index 9354556719..99574f34a5 100644 --- a/docs/stylus/concepts/gas-metering.mdx +++ b/docs/stylus/concepts/gas-metering.mdx @@ -5,7 +5,7 @@ author: rachel-bousfield sme: rachel-bousfield target_audience: 'Developers deploying smart contracts using Stylus.' sidebar_position: 3 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- **Gas and ink** are the pricing primitives that are used to determine the cost of handling specific opcodes and host I/Os on Stylus. For an overview of specific opcode and host I/O costs, see [Gas and ink costs](/stylus/reference/opcode-hostio-pricing). @@ -76,4 +76,4 @@ However, developers optimizing contracts may choose to measure performance in in ### See also - [Gas and ink costs](/stylus/reference/opcode-hostio-pricing): Detailed costs per opcode and host I/O -- [Caching strategy](/stylus/how-tos/caching-contracts): Description of the Stylus caching strategy and the `CacheManager` contract +- [Caching strategy](/stylus/guides/caching-contracts): Description of the Stylus caching strategy and the `CacheManager` contract diff --git a/docs/stylus/concepts/public-preview-expectations.mdx b/docs/stylus/concepts/public-preview-expectations.mdx new file mode 100644 index 0000000000..3ff00bc060 --- /dev/null +++ b/docs/stylus/concepts/public-preview-expectations.mdx @@ -0,0 +1,42 @@ +--- +title: 'Public preview: what to expect' +description: 'Stylus is currently tagged as a `release-candidate` supported by *public preview* documentation. This concept document explains what this means, and what to expect.' +author: symbolpunk +sidebar_position: 10 +--- + +Stylus is currently tagged as a `release-candidate` supported by _public preview_ documentation. This concept document explains what "public preview" means, what to expect from public preview capabilities, and how to engage with our team as you tinker. + +### How products are developed at Offchain Labs + +Offchain Labs builds products in a way that aligns loosely with the spirit of "building in public". We like to release things **early and often** so that we can capture feedback and iterate in service of your needs, as empirically as possible. + +To do this, some of our product offerings are documented with **public preview** disclaimers that look like this: + +This banner's purpose is to set expectations while inviting readers like you to express your needs so that we can incorporate them into the way that we iterate on product. + +### What to expect when using public preview offerings + +As you tinker and provide feedback, we'll be listening. Sometimes, we'll learn something non-obvious that will result in a significant change. More commonly, you'll experience incremental improvements to the developer experience as the offering grows out of its **public preview** status, towards **stable** status. + +Public preview offerings are evolving rapidly, so don't expect the degree of release notes discipline that you'd expect from a stable offering. Keep your eyes open for notifications regarding patch, minor, and major changes, along with corresponding relnotes that highlight breaking changes and new capabilities. + +### How to provide feedback + +Our product team primarily uses three feedback channels while iterating on public preview capabilities: + +1. **Docs**: Click on the **Request an update** button located in the top-right corner of any document to provide feedback on the docs and/or developer experience. This will lead you to a prefilled Github issue that members of our product team periodically review. +2. **Discord**: [Join the Arbitrum Discord](https://discord.gg/arbitrum) to engage with members of the Arbitrum community and product team. +3. **Google form**: Complete [this form](http://bit.ly/3yy6EUK) to ask for support. + +### What to expect when providing feedback + +Our ability to respond to feedback is determined by our ever-evolving capacity and priorities. We can't guarantee responses to all feedback submissions, but our small-but-mighty team is listening, and we'll try our best to acknowledge and respond to your feedback. No guarantees though! + +:::info +[Our small-but-mighty team is hiring](https://jobs.lever.co/offchainlabs). +::: + +### Thank you! + +Thanks for helping us build things that meet your needs! We're excited to engage with OGs and newcomers alike; please don't hesitate to reach out. diff --git a/docs/stylus/concepts/vm-differences.mdx b/docs/stylus/concepts/vm-differences.mdx new file mode 100644 index 0000000000..dd5008f529 --- /dev/null +++ b/docs/stylus/concepts/vm-differences.mdx @@ -0,0 +1,641 @@ +--- +title: 'VM and execution differences' +sidebar_label: 'VM differences' +description: 'Understand the differences between EVM and WASM execution models in Stylus' +user_story: 'As a developer, I want to understand how WASM execution differs from EVM' +content_type: concept +author: chrisco +sme: chrisco +sidebar_position: 4 +target_audience: Developers who need to understand how Stylus works with EVM differences. +displayed_sidebar: buildStylusSidebar +--- + +Arbitrum Nitro supports two execution environments: the traditional Ethereum Virtual Machine (EVM) for Solidity contracts and a WebAssembly (WASM) VM for Stylus contracts. While both environments are fully interoperable and share the same state, they differ significantly in their execution models, performance characteristics, and developer experience. + +## Execution model + +### EVM: Stack-based architecture + +The EVM uses a stack-based execution model: + +- **Operations**: Work with values on a stack (PUSH, POP, ADD, etc.) +- **Opcodes**: 256 predefined opcodes with fixed gas costs +- **Memory**: Linear, byte-addressable memory that grows dynamically +- **Storage**: 256-bit word-based key-value store +- **Call depth**: Limited to 1024 levels + +**Example EVM execution:** + +``` +PUSH1 0x02 // Push 2 onto stack +PUSH1 0x03 // Push 3 onto stack +ADD // Pop 2 values, push sum (5) +``` + +### WASM: Register-based architecture + +The Stylus WASM VM uses a register-based execution model: + +- **Operations**: Work with virtual registers and local variables +- **Instructions**: Thousands of WASM instructions with fine-grained metering +- **Memory**: Linear memory with explicit grow operations +- **Storage**: Same 256-bit storage as EVM (shared state) +- **Call depth**: Same 1024 limit for compatibility + +**Example WASM execution:** + +```wasm +(local.get 0) ;; Read from local variable 0 +(local.get 1) ;; Read from local variable 1 +(i32.add) ;; Add and store result +(local.set 2) ;; Store in local variable 2 +``` + +## Memory model + +### EVM memory + +- **Dynamic expansion**: Memory grows in 32-byte chunks +- **Gas cost**: Quadratic growth (memory expansion gets expensive) +- **Access pattern**: Byte-level addressing +- **Limit**: Practical limit around 15 MB due to gas costs + +### WASM memory + +- **Page-based**: Memory grows in 64 KB pages (WASM standard) +- **Gas cost**: Linear cost per page through `pay_for_memory_grow` +- **Access pattern**: Direct memory load/store instructions +- **Limit**: Can grow much larger efficiently + +**Memory growth in Stylus:** + +```rust +// The entrypoint macro automatically handles pay_for_memory_grow +#[entrypoint] +pub struct MyContract { + // Large data structures are more practical in WASM + data: StorageVec, +} + +// Nitro automatically inserts pay_for_memory_grow calls +// when allocating new pages +let large_vector = vec![0u8; 100_000]; // Efficient in WASM +``` + +:::note +The Stylus SDK's `entrypoint!` macro includes a no-op call to `pay_for_memory_grow` to ensure the function is referenced. Nitro then automatically inserts actual calls when memory allocation occurs. +::: + +## Gas metering: Ink and gas + +### EVM gas metering + +- **Unit**: Gas (standard Ethereum unit) +- **Granularity**: Per opcode (e.g., ADD = 3 gas, SSTORE = 20,000 gas) +- **Measurement**: Coarse-grained +- **Refunds**: Available for storage deletions + +### Stylus ink metering + +Stylus introduces "ink" as a fine-grained metering unit: + +- **Unit**: Ink (Stylus-specific, converted to gas) +- **Granularity**: Per WASM instruction (more fine-grained) +- **Measurement**: Precise tracking of WASM execution costs +- **Conversion**: Ink → Gas conversion happens automatically + +**Ink to gas conversion:** + +```rust +// Check remaining ink +let ink_left = evm_ink_left(); + +// Check remaining gas +let gas_left = evm_gas_left(); + +// Get ink price (in gas basis points) +let ink_price = tx_ink_price(); + +// Conversion formula: +// gas = ink * ink_price / 10000 +``` + +**Why ink?** + +1. **Precision**: WASM instructions have varying costs that don't map cleanly to EVM gas +2. **Efficiency**: Fine-grained metering allows for more accurate pricing +3. **Performance**: Enables cheaper execution for compute-heavy operations +4. **Flexibility**: Ink prices can be adjusted without changing contract code + +**Gas cost comparison:** + +| Operation | EVM Gas | Stylus Gas | Improvement | +| ------------------- | --------- | ---------- | --------------- | +| Basic arithmetic | 3-5 | ~1-2 | 2-3x cheaper | +| Memory operations | Variable | Efficient | 10-100x cheaper | +| Complex computation | Expensive | Cheap | 10-100x cheaper | +| Storage operations | Same | Same | Equal | +| External calls | Same | Same | Equal | + +## Instruction sets + +### EVM opcodes + +- **Count**: ~140 opcodes +- **Categories**: Arithmetic, logic, storage, flow control, system +- **Size**: 1 byte per opcode +- **Examples**: + - `ADD`, `MUL`, `SUB`, `DIV` (arithmetic) + - `SLOAD`, `SSTORE` (storage) + - `CALL`, `DELEGATECALL` (calls) + - `SHA3` (hashing) + +### WASM instructions + +- **Count**: Hundreds of instructions +- **Categories**: Numeric, memory, control flow, function calls +- **Size**: Variable encoding (1-5 bytes) +- **Examples**: + - `i32.add`, `i64.mul`, `f64.div` (numeric) + - `memory.grow`, `memory.size` (memory) + - `call`, `call_indirect` (functions) + - Hostio imports (system operations) + +**WASM advantages:** + +- More expressive instruction set +- Better compiler optimization targets +- Efficient handling of complex data structures +- Native support for 32-bit and 64-bit operations + +## Size limits + +### EVM contracts + +- **Maximum size**: 24,576 bytes (24 KB) of deployed bytecode +- **Limit reason**: Block gas limit and deployment costs +- **Workaround**: Contract splitting, proxies + +### Stylus contracts + +- **Initial limit**: 24 KB (same as EVM for compatibility) +- **Compressed size**: Can be larger before compression +- **Future**: Limit may be increased as WASM tooling improves +- **Practical size**: Stylus programs are often smaller due to efficient compilation + +**Size optimization:** + +```rust +// Stylus contracts benefit from: +// 1. Rust's zero-cost abstractions +// 2. Dead code elimination by wasm-opt +// 3. Efficient WASM encoding + +#[no_std] // Opt out of standard library for smaller binaries +extern crate alloc; + +// Only the code actually used is included +use stylus_sdk::prelude::*; +``` + +## Storage model + +Both EVM and WASM contracts use the **same storage system**: + +- **Format**: 256-bit key-value store +- **Compatibility**: EVM and WASM contracts can share storage +- **Costs**: SLOAD and SSTORE costs are identical +- **Caching**: Stylus VM implements storage caching for efficiency + +### Storage caching in Stylus + +```rust +use stylus_sdk::prelude::*; + +#[storage] +pub struct Counter { + count: StorageU256, +} + +#[public] +impl Counter { + pub fn increment(&mut self) { + // First read: full SLOAD cost + let current = self.count.get(); + + // Write is cached + self.count.set(current + U256::from(1)); + + // Additional reads in same call are cheaper (cached) + let new_value = self.count.get(); + + // Cache is automatically flushed at call boundary + } +} +``` + +**Cache benefits:** + +1. **Reduced gas costs**: Repeated reads are cheaper +2. **Better performance**: Fewer state trie accesses +3. **Automatic management**: SDK handles cache flushing +4. **Compatibility**: Refund logic matches EVM exactly + +## Performance characteristics + +### Compute operations + +| Category | EVM | Stylus WASM | Winner | +| ------------------- | --------- | ----------- | ------------ | +| Integer arithmetic | Moderate | Fast | WASM (10x+) | +| Loops | Expensive | Cheap | WASM (100x+) | +| Memory copying | Expensive | Cheap | WASM (10x+) | +| Hashing (keccak256) | Native | Native | Equal | +| Cryptography | Limited | Efficient | WASM | +| String operations | Expensive | Cheap | WASM (100x+) | + +### Storage operations + +| Operation | EVM | Stylus WASM | Winner | +| --------------- | ---------- | ----------- | ------ | +| SLOAD | 2,100 gas | 2,100 gas | Equal | +| SSTORE (new) | 20,000 gas | 20,000 gas | Equal | +| SSTORE (update) | 5,000 gas | 5,000 gas | Equal | +| Storage refunds | Standard | Standard | Equal | +| Cached reads | No | Yes | WASM | + +### External interactions + +| Operation | EVM | Stylus WASM | Winner | +| -------------------- | ------------- | ------------- | ------ | +| Contract calls | ~700 gas base | ~700 gas base | Equal | +| Cross-language calls | N/A | Efficient | WASM | +| Event emission | Same cost | Same cost | Equal | +| Value transfers | Same cost | Same cost | Equal | + +## Call semantics + +### Interoperability + +Both environments support seamless interoperability: + +```rust +// Stylus calling Solidity +sol_interface! { + interface IERC20 { + function transfer(address to, uint256 amount) external returns (bool); + } +} + +#[public] +impl MyContract { + pub fn call_evm_contract(&self, token: Address) -> Result> { + let erc20 = IERC20::new(token); + let result = erc20.transfer(self.vm(), recipient, amount)?; + Ok(result) + } +} +``` + +```solidity +// Solidity calling Stylus +interface IStylusContract { + function computeHash(bytes calldata data) external view returns (bytes32); +} + +contract EvmContract { + function useStylus(address stylusAddr, bytes calldata data) public view returns (bytes32) { + return IStylusContract(stylusAddr).computeHash(data); + } +} +``` + +### Call costs + +- **Same call overhead**: Both directions have similar base costs +- **ABI encoding**: Identical for both +- **Gas forwarding**: Follows 63/64 rule in both cases +- **Return data**: Handled consistently + +## Contract lifecycle + +### Deployment + +**EVM contracts:** + +1. Submit init code (constructor bytecode) +2. EVM executes init code +3. Returns runtime bytecode +4. Bytecode stored onchain + +**Stylus contracts:** + +1. Compile Rust → WASM +2. Submit WASM code +3. **Activation step**: One-time compilation to native code +4. Activated programs cached for efficiency +5. WASM code stored onchain + +**Activation benefits:** + +```shell +# Deploy and activate a Stylus program +cargo stylus deploy --private-key $PRIVATE_KEY + +# Activation happens once +# Subsequent calls use cached native code +``` + +- **One-time cost**: Pay activation gas once +- **Future savings**: All executions use optimized native code +- **Upgradeability**: Re-activation needed for upgrades + +### Execution flow + +**EVM contracts:** + +``` +Transaction → EVM → Opcode interpretation → State changes +``` + +**Stylus contracts:** + +``` +Transaction → WASM VM → Native code execution → State changes + ↓ + Hostio calls for state access +``` + +## Developer experience + +### EVM development + +**Languages**: Solidity, Vyper, Huff + +**Tools**: + +- Hardhat, Foundry for testing +- Remix for quick development +- Ethers.js/Web3.js for interaction + +**Debugging**: + +- Revert messages +- Events for tracing +- Stack traces limited + +### Stylus development + +**Languages**: Rust, C, C++ (any WASM-compatible language) + +**Tools**: + +- `cargo stylus` for deployment +- Standard Rust tooling (cargo, rustc) +- `TestVM` for unit testing +- Rust analyzer for IDE support + +**Debugging**: + +- Full Rust error messages +- Compile-time safety checks +- `console!` macro for debug builds +- Stack traces in development + +**Development comparison:** + +| Aspect | EVM | Stylus | Notes | +| --------------- | -------------- | ------------------- | ------------------------------------- | +| Type safety | Runtime | Compile-time | Rust catches errors before deployment | +| Memory safety | Manual | Automatic | Rust's borrow checker | +| Testing | External tools | Built-in Rust tests | `#[test]` functions work natively | +| Iteration speed | Slower | Faster | No need to redeploy for tests | +| Learning curve | Moderate | Steeper | Rust has more concepts | +| Maturity | Very mature | Growing | Solidity has more resources | + +## Feature compatibility + +### Supported features + +Both EVM and Stylus support: + +✅ Contract calls and delegate calls +✅ Value transfers +✅ Event emission +✅ Storage operations +✅ Block and transaction properties +✅ Cryptographic functions (keccak256) +✅ Contract creation (CREATE, CREATE2) +✅ Revert and error handling +✅ Reentrancy guards + +### EVM-specific features not in WASM + +❌ Inline assembly (use hostio or Rust instead) +❌ `selfdestruct` (deprecated in Ethereum anyway) +❌ Solidity modifiers (use Rust functions) +❌ Multiple inheritance (use traits and composition) + +### Stylus-specific features not in EVM + +✅ Access to Rust ecosystem (crates) +✅ Efficient memory management +✅ Zero-cost abstractions +✅ Compile-time guarantees +✅ Native testing support +✅ Better optimization opportunities + +## State sharing + +EVM and WASM contracts share the same blockchain state: + +```rust +// Stylus contract can read EVM contract storage +#[storage] +pub struct Bridge { + evm_contract: StorageAddress, +} + +#[public] +impl Bridge { + pub fn read_evm_storage(&self, key: U256) -> U256 { + // Both VMs use the same storage layout + // Can read storage written by EVM contracts + storage_load_bytes32(key) + } +} +``` + +**Shared state:** + +- Account balances +- Contract storage +- Contract code +- Transaction history +- Block data + +## Gas economics + +### Cost structure + +**EVM contract execution:** + +``` +Total cost = Base transaction cost (21,000 gas) + + Input data cost (~16 gas/byte) + + Execution cost (opcode gas) + + Storage cost (SLOAD/SSTORE) +``` + +**Stylus contract execution:** + +``` +Total cost = Base transaction cost (21,000 gas) + + Input data cost (~16 gas/byte) + + Execution cost (ink → gas conversion) + + Storage cost (same as EVM) +``` + +### When to use each + +**Use EVM (Solidity) when:** + +- Quick prototyping needed +- Simple contracts with minimal computation +- Team expertise in Solidity +- Extensive storage operations (cost is equal) +- Maximum ecosystem compatibility + +**Use Stylus (Rust) when:** + +- Compute-intensive operations +- Complex algorithms or data structures +- Need for memory safety guarantees +- Existing Rust codebase to port +- Optimizing for gas efficiency +- Cryptographic operations +- String/byte manipulation + +## Best practices + +### For EVM contracts + +1. **Minimize storage operations**: Use memory when possible +2. **Optimize loops**: Keep iterations minimal +3. **Pack storage**: Use smaller types when possible +4. **Avoid complex math**: Basic operations only +5. **Use libraries**: Leverage audited code + +### For Stylus contracts + +1. **Leverage Rust's safety**: Let the compiler catch bugs +2. **Use iterators**: More efficient than manual loops +3. **Profile before optimizing**: Use cargo-stylus tools +4. **Test thoroughly**: Use Rust's built-in test framework +5. **Consider binary size**: Use `#[no_std]` if needed +6. **Batch operations**: Take advantage of cheap compute + +### Hybrid approach + +Many projects can benefit from both: + +```rust +// Compute-heavy logic in Stylus +#[public] +impl ComputeEngine { + pub fn complex_calculation(&self, data: Vec) -> Vec { + // Efficient loops and data processing + data.iter() + .map(|x| expensive_computation(*x)) + .collect() + } +} +``` + +```solidity +// Coordination and state management in Solidity +contract Coordinator { + IComputeEngine public engine; // Stylus contract + + function process(uint256[] calldata data) public { + uint256[] memory results = engine.complex_calculation(data); + // Store results, emit events, etc. + } +} +``` + +## Migration considerations + +### From Solidity to Stylus + +**What stays the same:** + +- Contract addresses +- Storage layout +- ABIs and interfaces +- Gas for storage operations +- Event signatures + +**What changes:** + +- Programming language (Solidity → Rust) +- Execution engine (EVM → WASM) +- Gas costs for compute (usually cheaper) +- Development workflow +- Testing approach + +**Migration strategy:** + +1. Start with compute-heavy functions +2. Maintain same ABI for compatibility +3. Test extensively with existing contracts +4. Monitor gas costs in production +5. Gradually migrate more functionality + +## Future developments + +### EVM evolution + +- EIP improvements +- New opcodes +- Gas repricing +- EOF (EVM Object Format) + +### Stylus evolution + +- Support for more languages +- SIMD instructions +- Floating point operations +- Larger contract size limits +- Further gas optimizations +- Enhanced debugging tools + +## Resources + +- [Stylus documentation](https://docs.arbitrum.io/stylus) +- [Ink and gas metering](https://docs.arbitrum.io/stylus/concepts/gas-metering) +- [WASM specification](https://webassembly.github.io/spec/) +- [EVM opcodes reference](https://www.evm.codes/) +- [Stylus SDK repository](https://github.com/OffchainLabs/stylus-sdk-rs) + +## Summary + +The WASM VM in Arbitrum Nitro represents a significant evolution in smart contract execution: + +**Key advantages of WASM:** + +- 10-100x cheaper for compute operations +- More expressive programming languages +- Better memory management +- Compile-time safety guarantees +- Access to mature language ecosystems + +**Key advantages of EVM:** + +- Mature tooling and ecosystem +- Familiar to existing developers +- No activation cost +- Decades of collective knowledge + +Both execution environments coexist harmoniously on Arbitrum, allowing developers to choose the best tool for each use case while maintaining full interoperability. diff --git a/docs/stylus/concepts/webassembly.mdx b/docs/stylus/concepts/webassembly.mdx new file mode 100644 index 0000000000..c1ead592ad --- /dev/null +++ b/docs/stylus/concepts/webassembly.mdx @@ -0,0 +1,412 @@ +--- +title: 'WebAssembly in Nitro' +description: 'Understanding WebAssembly compilation, deployment, and execution in Arbitrum Nitro' +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers who need to understand how Stylus works with WebAssembly. +displayed_sidebar: buildStylusSidebar +--- + +WebAssembly (WASM) is a binary instruction format that enables high-performance execution of programs in the Nitro virtual machine. This guide explains how WASM works in the context of Arbitrum Nitro and Stylus smart contract development. + +## What is WebAssembly? + +WebAssembly is a portable, size-efficient binary format designed for safe execution at near-native speeds. Key characteristics include: + +- **Binary format**: Compact representation that's faster to parse than text-based formats +- **Stack-based VM**: Simple execution model with operand stack +- **Sandboxed execution**: Memory-safe by design with explicit bounds checking +- **Language-agnostic**: Can be targeted by many programming languages (Rust, C, C++, etc.) + +## Why WebAssembly in Nitro? + +Nitro uses WebAssembly as its execution environment for several reasons: + +1. **Performance**: WASM compiles to native machine code for fast execution +2. **Security**: Sandboxed environment prevents unauthorized access +3. **Portability**: Same bytecode runs identically across all nodes +4. **Language flexibility**: Developers can use Rust, C, C++, or any language that compiles to WASM +5. **Determinism**: Guaranteed identical execution across all validators + +![WASM Execution Pipeline](/img/stylus-wasm-compile.png) + +_Figure: WebAssembly execution pipeline in Arbitrum Nitro, from source code to native execution with access to blockchain state._ + +## WASM compilation target + +Stylus contracts are compiled to the `wasm32-unknown-unknown` target, which means: + +- **32-bit addressing**: Uses 32-bit pointers and memory addresses +- **Unknown OS**: No operating system dependencies +- **Unknown environment**: Minimal runtime assumptions (no std by default) + +The `.cargo/config.toml` file in Stylus projects configures the WASM target: + +```toml +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "link-arg=-zstack-size=32768", # 32KB stack + "-C", "target-feature=-reference-types", # Disable reference types + "-C", "target-feature=+bulk-memory", # Enable bulk memory operations +] +``` + +### Compilation flags + +- **Stack size**: Limited to 32KB to ensure bounded memory usage +- **Bulk memory**: Enables efficient `memory.copy` and `memory.fill` operations +- **No reference types**: Keeps the WASM simpler and more compatible + +## WASM binary structure + +A Stylus WASM module consists of several sections: + +![WASM Module Structure](/img/stylus-wasm-deploy.png) + +_Figure: WASM module structure showing the main sections including exports, imports (hostio functions), memory, and code._ + +### Exports + +Every Stylus contract exports a `user_entrypoint` function: + +```rust +#[no_mangle] +pub extern "C" fn user_entrypoint(len: usize) -> usize { + // Entry point for all contract calls + // len: size of calldata in bytes + // returns: size of output data in bytes +} +``` + +This function is automatically generated by the `#[entrypoint]` macro and serves as the single entry point for all contract interactions. + +### Imports + +WASM modules import low-level functions from the `vm_hooks` module: + +```rust +// Example hostio imports +extern "C" { + fn storage_load_bytes32(key: *const u8, dest: *mut u8); + fn storage_store_bytes32(key: *const u8, value: *const u8); + fn msg_sender(sender: *mut u8); + fn block_timestamp() -> u64; + // ... and many more +} +``` + +These imported functions (called "hostio" functions) provide access to blockchain state and functionality. + +### Memory + +WASM modules use linear memory, which is: + +- **Contiguous**: Single continuous address space starting at 0 +- **Growable**: Can expand at runtime (in 64KB pages) +- **Isolated**: Each contract has its own memory space + +Memory growth is explicitly metered: + +```rust +// Exported function that must exist +#[no_mangle] +pub extern "C" fn pay_for_memory_grow(pages: u16) { + // Called before memory.grow to charge for new pages + // Each page is 64KB +} +``` + +### Custom sections + +WASM supports custom sections for metadata: + +```rust +// Example: Add version information +#[link_section = ".custom.stylus-version"] +static VERSION: [u8; 5] = *b"0.1.0"; +``` + +Custom sections can store: + +- Contract version +- Source code hashes +- Compiler metadata +- ABI information + +## Compression and deployment + +Before deployment, Stylus contracts undergo compression: + +### Brotli compression + +```rust +// From stylus-tools/src/utils/wasm.rs +pub fn brotli_compress(wasm: impl Read, compression_level: u32) -> io::Result> { + let mut compressed = Vec::new(); + let mut encoder = brotli::CompressorWriter::new(&mut compressed, 4096, compression_level, 22); + io::copy(&mut wasm, &mut encoder)?; + encoder.flush()?; + Ok(compressed) +} +``` + +Brotli compression typically reduces WASM size by 50-70%. + +### 0xEFF000 prefix + +Compressed WASM is prefixed with `0xEFF000` to identify it as a Stylus program: + +```rust +pub fn add_prefix(compressed_wasm: impl IntoIterator, prefix: &str) -> Vec { + let prefix_bytes = hex::decode(prefix.strip_prefix("0x").unwrap_or(prefix)).unwrap(); + prefix_bytes.into_iter().chain(compressed_wasm).collect() +} +``` + +This prefix allows the Nitro VM to distinguish Stylus contracts from EVM bytecode. + +## Contract activation + +After deployment, contracts must be **activated** before execution: + +### Activation process + +1. **Initial deployment**: Contract code is stored onchain (compressed) +2. **Activation call**: Special transaction invokes `activateProgram` +3. **Decompression**: Brotli-compressed WASM is decompressed +4. **Validation**: WASM is checked for: + - Valid structure + - Required exports (`user_entrypoint`) + - Allowed imports (only `vm_hooks`) + - Memory constraints +5. **Compilation**: WASM is compiled to native machine code +6. **Caching**: Compiled code is cached for future executions + +### One-time cost + +Activation incurs a one-time gas cost but provides benefits: + +- **Fast execution**: Native code runs 10-100x faster than interpreted +- **Persistent cache**: Compilation happens once, benefits all future calls +- **Optimizations**: Native compiler applies target-specific optimizations + +### Verification + +The activation process checks for the `pay_for_memory_grow` function to verify correct entrypoint setup: + +```rust +// From activation.rs +if !wasm::has_entrypoint(&wasm)? { + bail!("WASM is missing the entrypoint export"); +} +``` + +## Development workflow + +### 1. Write Rust code + +```rust +use stylus_sdk::{alloy_primitives::U256, prelude::*}; + +#[entrypoint] +#[storage] +pub struct Counter { + count: StorageU256, +} + +#[public] +impl Counter { + pub fn increment(&mut self) { + let count = self.count.get() + U256::from(1); + self.count.set(count); + } +} +``` + +### 2. Compile to WASM + +```bash +cargo stylus build +``` + +This runs: + +```bash +cargo build \ + --lib \ + --locked \ + --release \ + --target wasm32-unknown-unknown \ + --target-dir target/wasm32-unknown-unknown/release +``` + +### 3. Optimize (optional) + +```bash +wasm-opt target/wasm32-unknown-unknown/release/my_contract.wasm \ + -O3 \ + --strip-debug \ + -o optimized.wasm +``` + +Optimization can reduce size by an additional 10-30%. + +### 4. Deploy and activate + +```bash +# Deploy compressed WASM +cargo stylus deploy --private-key=$PRIVATE_KEY + +# Activation happens automatically +``` + +## Size limitations + +Nitro imposes limits on WASM contract size: + +| Limit | Value | Reason | +| --------------------- | ---------------------- | --------------------------------- | +| **Uncompressed size** | ~3-4 MB | Memory and processing constraints | +| **Compressed size** | 24 KB (initial) | Ethereum transaction size limit | +| **Compressed size** | 128 KB (with EIP-4844) | Larger blob transactions | + +To stay within limits: + +- Use `#[no_std]` to avoid standard library bloat +- Strip debug symbols with `--strip-debug` +- Enable aggressive optimization (`-O3`) +- Minimize dependencies +- Use compact data structures + +## Memory model + +![WASM Memory Model](/img/stylus-wasm-execute.png) + +_Figure: WASM linear memory model showing the fixed 32KB stack and growable heap organized in 64KB pages._ + +### Linear memory layout + +``` +0x00000000 ┌─────────────────┐ + │ Stack │ 32 KB fixed size +0x00008000 ├─────────────────┤ + │ Heap/Data │ Grows upward + │ │ + │ (Available) │ + │ │ +0xFFFFFFFF └─────────────────┘ +``` + +### Memory operations + +```rust +// Bulk memory operations (enabled by target config) +unsafe { + // Fast memory copy + core::ptr::copy_nonoverlapping(src, dst, len); + + // Fast memory fill + core::ptr::write_bytes(ptr, value, len); +} +``` + +The `bulk-memory` feature flag enables efficient WASM instructions like `memory.copy` and `memory.fill`. + +## Advanced: WASM instructions + +Stylus uses WASM MVP (Minimum Viable Product) instructions plus bulk-memory operations: + +### Arithmetic + +- `i32.add`, `i32.sub`, `i32.mul`, `i32.div_s`, `i32.div_u` +- `i64.add`, `i64.sub`, `i64.mul`, `i64.div_s`, `i64.div_u` + +### Memory access + +- `i32.load`, `i32.store` (32-bit load/store) +- `i64.load`, `i64.store` (64-bit load/store) +- `memory.grow` (expand memory) +- `memory.copy` (bulk copy, requires flag) +- `memory.fill` (bulk fill, requires flag) + +### Control flow + +- `call`, `call_indirect` (function calls) +- `if`, `else`, `block`, `loop` (structured control flow) +- `br`, `br_if` (branching) + +### Not supported + +- ❌ Floating point operations (f32, f64) +- ❌ SIMD operations +- ❌ Reference types +- ❌ Multiple memories +- ❌ Threads + +## Best practices + +### 1. Minimize binary size + +```rust +// Use #[no_std] when possible +#![no_std] +extern crate alloc; + +// Avoid large dependencies +// Prefer: alloy-primitives +// Avoid: serde_json, regex (unless necessary) +``` + +### 2. Optimize memory usage + +```rust +// Stack allocate when possible +let small_buffer = [0u8; 32]; + +// Heap allocate only when necessary +let large_buffer = vec![0u8; 1024]; +``` + +### 3. Profile before optimizing + +```bash +# Check binary size +ls -lh target/wasm32-unknown-unknown/release/*.wasm + +# Analyze with twiggy +cargo install twiggy +twiggy top target/wasm32-unknown-unknown/release/my_contract.wasm +``` + +### 4. Test locally + +```bash +# Use cargo-stylus for local testing +cargo stylus check +cargo stylus export-abi +``` + +### 5. Validate before deployment + +```rust +// Ensure entrypoint exists +#[entrypoint] +#[storage] +pub struct MyContract { /* ... */ } + +// Verify required exports +#[no_mangle] +pub extern "C" fn pay_for_memory_grow(pages: u16) { + // Generated automatically by SDK +} +``` + +## Resources + +- [WebAssembly specification](https://webassembly.github.io/spec/) +- [Rust WASM target documentation](https://doc.rust-lang.org/rustc/platform-support/wasm32-unknown-unknown.html) +- [Stylus SDK repository](https://github.com/OffchainLabs/stylus-sdk-rs) +- [Cargo Stylus CLI tool](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) +- [WASM binary toolkit (wabt)](https://github.com/WebAssembly/wabt) +- [Binaryen optimization tools](https://github.com/WebAssembly/binaryen) diff --git a/docs/stylus/fundamentals/_category_.yml b/docs/stylus/fundamentals/_category_.yml new file mode 100644 index 0000000000..09fabd7c58 --- /dev/null +++ b/docs/stylus/fundamentals/_category_.yml @@ -0,0 +1,6 @@ +label: 'Fundamentals' +position: 2 +collapsed: false +link: + type: generated-index + description: Essential Stylus SDK concepts, prerequisites, and core development skills. diff --git a/docs/stylus/fundamentals/choose-your-path.mdx b/docs/stylus/fundamentals/choose-your-path.mdx new file mode 100644 index 0000000000..ec7ec8dfe0 --- /dev/null +++ b/docs/stylus/fundamentals/choose-your-path.mdx @@ -0,0 +1,261 @@ +--- +title: 'Choose your learning path' +sidebar_label: 'Choose your path' +description: 'Find the right Stylus learning path based on your background and goals' +user_story: 'As a developer new to Stylus, I want to find the most efficient learning path based on my background' +content_type: concept +author: offchainlabs +sme: offchainlabs +sidebar_position: 0 +--- + +# Choose your learning path + +Not sure where to start with Stylus? This guide helps you find the optimal learning path based on your background and goals. + +## Quick path selector + +
+Path 1: New to Rust + +**Best for**: Developers experienced with other languages but new to Rust. + +### Prerequisites + +- Basic programming knowledge +- Familiarity with concepts like variables, functions, and control flow +- Optional: Smart contract development experience + +### Recommended journey + +1. **Learn Rust basics** (1-2 weeks) + + - Work through [The Rust Book](https://doc.rust-lang.org/book/) chapters 1-10 + - Focus on: ownership, borrowing, structs, enums, error handling + - Practice with [Rustlings](https://github.com/rust-lang/rustlings) + +2. **Start with Stylus** (2-3 days) + + - Complete the [Quickstart](/stylus/quickstart) + - Understand [project structure](/stylus/fundamentals/project-structure) + - Deploy your first contract + +3. **Build fundamentals** (1 week) + + - Study [data types](/stylus/fundamentals/data-types/primitives) + - Learn [storage patterns](/stylus/fundamentals/data-types/storage) + - Practice [writing tests](/stylus/fundamentals/testing-contracts) + +4. **Explore advanced topics** (ongoing) + - Review [best practices](/stylus/best-practices/security) + - Study [gas optimization](/stylus/best-practices/gas-optimization) + - Build real projects + +### Key resources + +- [The Rust Book](https://doc.rust-lang.org/book/) +- [Stylus Quickstart](/stylus/quickstart) +- [Stylus by Example](https://stylus-by-example.org/) + +--- + +
+ +
+Path 2: Solidity developer + +**Best for**: Experienced Solidity developers transitioning to Stylus. + +### Prerequisites + +- Strong Solidity knowledge +- Understanding of EVM and smart contract security +- No Rust experience required + +### Recommended journey + +1. **Understand the differences** (1 day) + + - Read [Rust to Solidity differences](/stylus/advanced/rust-to-solidity-differences) + - Review [VM differences](/stylus/concepts/vm-differences) + - Understand [type conversions](/stylus/fundamentals/data-types/conversions-between-types) + +2. **Quick start** (1-2 days) + + - Complete the [Quickstart](/stylus/quickstart) + - Compare with familiar Solidity patterns + - Deploy a simple contract + +3. **Learn Rust patterns** (3-5 days) + + - Study [ownership and borrowing](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) + - Learn [error handling](/stylus/fundamentals/data-types/primitives) + - Understand [storage macros](/stylus/fundamentals/data-types/storage) + +4. **Port existing contracts** (ongoing) + - Start with simple contracts + - Apply [security best practices](/stylus/best-practices/security) + - Optimize for [gas efficiency](/stylus/best-practices/gas-optimization) + +### Common migration patterns + +| Solidity Pattern | Stylus Equivalent | Guide | +| ----------------------------- | --------------------------- | ----------------------------------------------------------------------- | +| `mapping(address => uint)` | `StorageMap` | [Storage types](/stylus/fundamentals/data-types/storage) | +| `require(condition, "error")` | `ensure!(condition, Error)` | [Error handling](/stylus/fundamentals/contracts#error-handling) | +| `msg.sender` | `msg::sender()` | [Global functions](/stylus/fundamentals/global-variables-and-functions) | +| `constructor` | `impl` with initialization | [Contracts](/stylus/fundamentals/contracts) | +| Events | Derive `Erase` trait | See Stylus SDK documentation | + +### Key resources + +- [Rust to Solidity Differences](/stylus/advanced/rust-to-solidity-differences) +- [Stylus by Example](https://stylus-by-example.org/) +- [Solidity to Rust Cheat Sheet](https://docs.arbitrum.io/stylus) + +--- + +
+ +
+Path 3: Smart contract developer (non-Solidity) + +**Best for**: Developers experienced with other smart contract platforms (Move, Cairo, Clarity, etc.). + +### Prerequisites + +- Smart contract development experience +- Understanding of blockchain concepts +- Basic programming knowledge + +### Recommended journey + +1. **Jump right in** (1-2 days) + + - Complete the [Quickstart](/stylus/quickstart) + - Explore [project structure](/stylus/fundamentals/project-structure) + - Deploy and interact with a contract + +2. **Understand the execution model** (2-3 days) + + - Study [WebAssembly concepts](/stylus/concepts/webassembly) + - Review [VM differences](/stylus/concepts/vm-differences) + - Learn about [activation](/stylus/concepts/activation) + +3. **Master Rust for smart contracts** (1 week) + + - Focus on [storage patterns](/stylus/fundamentals/data-types/storage) + - Learn [contract structure](/stylus/fundamentals/contracts) + - Understand [testing approaches](/stylus/fundamentals/testing-contracts) + +4. **Advanced development** (ongoing) + - Apply platform-specific optimizations + - Implement cross-contract calls + - Build production applications + +### Key resources + +- [Quickstart](/stylus/quickstart) +- [WebAssembly Concepts](/stylus/concepts/webassembly) +- [Advanced Topics](/stylus/advanced/rust-to-solidity-differences) + +--- + +
+ +
+Path 4: Rust developer new to Web3 + +**Best for**: Experienced Rust developers entering blockchain development. + +### Prerequisites + +- Strong Rust knowledge +- Understanding of ownership, traits, and async programming +- No blockchain experience required + +### Recommended journey + +1. **Learn blockchain basics** (2-3 days) + + - Understand [smart contracts](https://ethereum.org/en/developers/docs/smart-contracts/) + - Learn about [gas and transactions](https://ethereum.org/en/developers/docs/gas/) + - Study [EVM basics](https://ethereum.org/en/developers/docs/evm/) + +2. **Quick start with Stylus** (1 day) + + - Complete the [Quickstart](/stylus/quickstart) + - Your Rust skills transfer directly! + - Deploy your first contract + +3. **Web3-specific patterns** (3-5 days) + + - Learn [storage patterns](/stylus/fundamentals/data-types/storage) + - Understand [global variables](/stylus/fundamentals/global-variables-and-functions) + - Study [security considerations](/stylus/best-practices/security) + +4. **Build applications** (ongoing) + - Start with simple DeFi primitives + - Implement events and logs + - Optimize for [gas efficiency](/stylus/best-practices/gas-optimization) + +### Rust skills that transfer well + +- **Ownership model**: Natural fit for secure contract design +- **Type safety**: Prevents common smart contract bugs +- **Error handling**: `Result` maps perfectly to contract errors +- **Testing**: Your testing skills apply directly +- **Performance optimization**: Critical for gas efficiency + +### Key resources + +- [Ethereum Smart Contracts](https://ethereum.org/en/developers/docs/smart-contracts/) +- [Stylus Quickstart](/stylus/quickstart) +- [Storage Patterns](/stylus/fundamentals/data-types/storage) + +--- + +
+ +## General learning resources + +Regardless of your path, these resources are valuable: + +### Official documentation + +- [Stylus Rust SDK Docs](https://docs.rs/stylus-sdk/latest/stylus_sdk/) +- [Cargo Stylus CLI](https://github.com/OffchainLabs/cargo-stylus) +- [Stylus by Example](https://stylus-by-example.org/) + +### Community and support + +- [Arbitrum Discord](https://discord.gg/arbitrum) +- [Developer Forums](https://forum.arbitrum.foundation/) +- [GitHub Discussions](https://github.com/OffchainLabs/stylus-sdk-rs/discussions) + +### Practice projects + +1. **Token contract**: Implement ERC-20 token standard +2. **NFT contract**: Build ERC-721 compatible NFT +3. **Simple DeFi**: Create a basic AMM or lending protocol +4. **DAO governance**: Implement voting and proposal system + +## Need help choosing? + +Still not sure which path is right for you? Consider these questions: + +- **Do you want to learn Rust?** → Path 1 or Path 4 +- **Do you already know Solidity?** → Path 2 +- **Are you experienced with other smart contract platforms?** → Path 3 +- **Do you want the fastest path to deployment?** → Path 2 or Path 3 +- **Do you want to deeply understand the technology?** → Path 1 or Path 4 + +## Next steps + +Ready to start? Begin with these essential guides: + +1. [Prerequisites and setup](/stylus/fundamentals/prerequisites) +2. [Quickstart: Deploy your first contract](/stylus/quickstart) +3. [Project structure](/stylus/fundamentals/project-structure) + +Good luck on your Stylus journey! diff --git a/docs/stylus/fundamentals/contracts.mdx b/docs/stylus/fundamentals/contracts.mdx new file mode 100644 index 0000000000..b5ba85e034 --- /dev/null +++ b/docs/stylus/fundamentals/contracts.mdx @@ -0,0 +1,1654 @@ +--- +title: 'Stylus contracts' +description: 'Stylus Rust SDK contracts, methods, and lifecycle' +author: chrisco +sme: chrisco +sidebar_position: 2 +target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +displayed_sidebar: buildStylusSidebar +--- + +Stylus smart contracts are fully compatible with Solidity contracts on Arbitrum chains. They compile to WebAssembly and share the same EVM state trie as Solidity contracts, enabling seamless interoperability. + +## Contract basics + +A Stylus contract consists of three main components: + +1. **Storage Definition**: Defines the contract's persistent state +2. **Entrypoint**: Marks the main contract struct that handles incoming calls +3. **Public Methods**: Functions exposed to external callers via the `#[public]` macro + +### Minimal Contract + +Here's the simplest possible Stylus contract: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use stylus_sdk::prelude::*; + +#[storage] +#[entrypoint] +pub struct HelloWorld; + +#[public] +impl HelloWorld { + fn user_main(_input: Vec) -> ArbResult { + Ok(Vec::new()) + } +} +``` + +This contract: + +- Uses `#[storage]` to define the contract struct (empty in this case) +- Uses `#[entrypoint]` to mark it as the contract's entry point +- Uses `#[public]` to expose the `user_main` function +- Returns `ArbResult`, which is `Result, Vec>` + +## Storage Definition + +Stylus contracts use the `sol_storage!` macro or `#[storage]` attribute to define persistent storage that maps directly to Solidity storage slots. + +### Using `sol_storage!` (Solidity-style) + +The `sol_storage!` macro lets you define storage using Solidity syntax: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +sol_storage! { + #[entrypoint] + pub struct Counter { + uint256 count; + address owner; + mapping(address => uint256) balances; + } +} +``` + +This creates a contract with: + +- A `count` field of type `StorageU256` +- An `owner` field of type `StorageAddress` +- A `balances` mapping from `Address` to `StorageU256` + +### Using `#[storage]` (Rust-style) + +Alternatively, use the `#[storage]` attribute with explicit storage types: + +```rust +use stylus_sdk::prelude::*; +use stylus_sdk::storage::{StorageU256, StorageAddress, StorageMap}; +use alloy_primitives::{Address, U256}; + +#[storage] +#[entrypoint] +pub struct Counter { + count: StorageU256, + owner: StorageAddress, + balances: StorageMap, +} +``` + +Both approaches produce identical storage layouts and are fully interoperable with Solidity contracts using the same storage structure. + +## The `#[entrypoint]` Macro + +The `#[entrypoint]` macro marks a struct as the contract's main entry point. It automatically implements the `TopLevelStorage` trait, which enables: + +- Routing incoming calls to public methods +- Managing contract storage +- Handling reentrancy protection (unless the `reentrant` feature is enabled) + +**Key requirements:** + +- Exactly one struct per contract must have `#[entrypoint]` +- The struct must also have `#[storage]` or be defined in `sol_storage!` +- The entrypoint struct represents the contract's root storage + +**Example:** + +```rust +sol_storage! { + #[entrypoint] + pub struct MyContract { + uint256 value; + } +} +``` + +The `#[entrypoint]` macro generates: + +1. An implementation of `TopLevelStorage` for the struct +2. A `user_entrypoint` function that Stylus calls when the contract receives a transaction +3. Method routing logic to dispatch calls to `#[public]` methods + +## Public Methods with `#[public]` + +The `#[public]` macro exposes Rust methods as external contract functions callable from Solidity, other Stylus contracts, or external callers. + +### Basic Public Methods + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::U256; + +sol_storage! { + #[entrypoint] + pub struct Calculator { + uint256 result; + } +} + +#[public] +impl Calculator { + // View function (read-only) + pub fn get_result(&self) -> U256 { + self.result.get() + } + + // Write function (mutates state) + pub fn set_result(&mut self, value: U256) { + self.result.set(value); + } + + // Pure function (no state access) + pub fn add(a: U256, b: U256) -> U256 { + a + b + } +} +``` + +### State Mutability + +The SDK automatically infers state mutability from the method signature: + +| Signature | Mutability | Solidity Equivalent | Description | +| ----------- | ---------- | ------------------- | --------------------- | +| `&self` | `view` | `view` | Read contract state | +| `&mut self` | Write | (default) | Modify contract state | +| Neither | `pure` | `pure` | No state access | + +**Examples:** + +```rust +#[public] +impl MyContract { + // View: can read state, cannot modify + pub fn balance_of(&self, account: Address) -> U256 { + self.balances.get(account) + } + + // Write: can read and modify state + pub fn transfer(&mut self, to: Address, amount: U256) { + let sender = self.vm().msg_sender(); + let balance = self.balances.get(sender); + self.balances.setter(sender).set(balance - amount); + self.balances.setter(to).set(self.balances.get(to) + amount); + } + + // Pure: no state access at all + pub fn calculate_fee(amount: U256) -> U256 { + amount * U256::from(3) / U256::from(100) + } +} +``` + +## Constructor + +The `#[constructor]` attribute marks a function that runs once during contract deployment. + +### Basic Constructor + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +sol_storage! { + #[entrypoint] + pub struct Token { + address owner; + uint256 total_supply; + } +} + +#[public] +impl Token { + #[constructor] + pub fn constructor(&mut self, initial_supply: U256) { + let deployer = self.vm().msg_sender(); + self.owner.set(deployer); + self.total_supply.set(initial_supply); + } + + pub fn owner(&self) -> Address { + self.owner.get() + } +} +``` + +### Constructor Features + +**Payable Constructor:** + +```rust +#[public] +impl Token { + #[constructor] + #[payable] + pub fn constructor(&mut self, initial_supply: U256) { + // Contract can receive ETH during deployment + let received = self.vm().msg_value(); + self.owner.set(self.vm().msg_sender()); + self.total_supply.set(initial_supply); + } +} +``` + +**Important Notes:** + +- The constructor name can be anything (doesn't have to be `constructor`) +- Only one constructor per contract +- Constructor runs exactly once when the contract is deployed +- Use `tx_origin()` instead of `msg_sender()` when deploying via a factory contract + +## Method Attributes + +### `#[payable]` + +Marks a function as able to receive ETH: + +```rust +#[public] +impl PaymentProcessor { + #[payable] + pub fn deposit(&mut self) -> U256 { + let sender = self.vm().msg_sender(); + let amount = self.vm().msg_value(); + + let current = self.balances.get(sender); + self.balances.setter(sender).set(current + amount); + + amount + } + + // Non-payable function will revert if ETH is sent + pub fn withdraw(&mut self, amount: U256) { + // Will revert if msg.value > 0 + let sender = self.vm().msg_sender(); + let balance = self.balances.get(sender); + self.balances.setter(sender).set(balance - amount); + } +} +``` + +**Important:** Without `#[payable]`, sending ETH to a function causes a revert. + +### `#[receive]` + +Handles plain ETH transfers without calldata (equivalent to Solidity's `receive()` function): + +```rust +use alloy_sol_types::sol; + +sol! { + event EtherReceived(address indexed sender, uint256 amount); +} + +#[public] +impl Wallet { + #[receive] + #[payable] + pub fn receive(&mut self) -> Result<(), Vec> { + let sender = self.vm().msg_sender(); + let amount = self.vm().msg_value(); + + let balance = self.balances.get(sender); + self.balances.setter(sender).set(balance + amount); + + self.vm().log(EtherReceived { sender, amount }); + Ok(()) + } +} +``` + +**Notes:** + +- Must be combined with `#[payable]` +- Called when the contract receives ETH without calldata +- Only one `#[receive]` function per contract +- Must have signature: `fn name(&mut self) -> Result<(), Vec>` + +### `#[fallback]` + +Handles calls to non-existent functions or as a fallback for ETH transfers: + +```rust +use alloy_sol_types::sol; + +sol! { + event FallbackCalled(address indexed sender, bytes4 selector, uint256 value); +} + +#[public] +impl Contract { + #[fallback] + #[payable] + pub fn fallback(&mut self, calldata: &[u8]) -> ArbResult { + let sender = self.vm().msg_sender(); + let value = self.vm().msg_value(); + + // Extract function selector if present + let selector = if calldata.len() >= 4 { + [calldata[0], calldata[1], calldata[2], calldata[3]] + } else { + [0; 4] + }; + + self.vm().log(FallbackCalled { + sender, + selector: selector.into(), + value, + }); + + Ok(vec![]) + } +} +``` + +**Fallback is called when:** + +1. A function call doesn't match any existing function signature +2. Plain ETH transfer when no `#[receive]` function exists +3. The contract receives calldata but no function matches + +**Notes:** + +- Must have signature: `fn name(&mut self, calldata: &[u8]) -> ArbResult` +- Can optionally include `#[payable]` to accept ETH +- Only one `#[fallback]` function per contract + +### `#[selector]` + +Customizes the Solidity function selector: + +```rust +#[public] +impl Token { + // Use a custom name in the ABI + #[selector(name = "balanceOf")] + pub fn get_balance(&self, account: Address) -> U256 { + self.balances.get(account) + } + + // Explicitly set the 4-byte selector + #[selector(bytes = "0x70a08231")] + pub fn balance_of_custom(&self, account: Address) -> U256 { + self.balances.get(account) + } +} +``` + +This is useful for: + +- Matching existing Solidity interfaces exactly +- Avoiding naming conflicts +- Implementing multiple methods with the same name but different selectors + +## Contract trait-based composition + +Stylus supports code reuse via trait-based composition. + +Define reusable functionality as traits and implement them on your contract: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +// Define interface traits +#[public] +trait IOwnable { + fn owner(&self) -> Address; + fn transfer_ownership(&mut self, new_owner: Address) -> bool; +} + +#[public] +trait IErc20 { + fn name(&self) -> String; + fn symbol(&self) -> String; + fn balance_of(&self, account: Address) -> U256; + fn transfer(&mut self, to: Address, value: U256) -> bool; +} + +// Define storage components +#[storage] +struct Ownable { + owner: StorageAddress, +} + +#[storage] +struct Erc20 { + balances: StorageMap, +} + +// Compose into main contract +#[storage] +#[entrypoint] +struct MyToken { + ownable: Ownable, + erc20: Erc20, +} + +// Declare which interfaces this contract implements +#[public] +#[implements(IOwnable, IErc20)] +impl MyToken {} + +// Implement each trait +#[public] +impl IOwnable for MyToken { + fn owner(&self) -> Address { + self.ownable.owner.get() + } + + fn transfer_ownership(&mut self, new_owner: Address) -> bool { + let caller = self.vm().msg_sender(); + if caller != self.ownable.owner.get() { + return false; + } + self.ownable.owner.set(new_owner); + true + } +} + +#[public] +impl IErc20 for MyToken { + fn name(&self) -> String { + "MyToken".into() + } + + fn symbol(&self) -> String { + "MTK".into() + } + + fn balance_of(&self, account: Address) -> U256 { + self.erc20.balances.get(account) + } + + fn transfer(&mut self, to: Address, value: U256) -> bool { + let from = self.vm().msg_sender(); + let from_balance = self.erc20.balances.get(from); + if from_balance < value { + return false; + } + self.erc20.balances.setter(from).set(from_balance - value); + let to_balance = self.erc20.balances.get(to); + self.erc20.balances.setter(to).set(to_balance + value); + true + } +} +``` + +**Benefits:** + +- Clear separation of concerns +- Explicit interface declarations +- Type-safe composition +- Easy to test components independently +- Compatible with Solidity interface standards + +### Accessing VM Context + +All public methods can access blockchain context via `self.vm()`: + +```rust +#[public] +impl MyContract { + pub fn get_caller_info(&self) -> (Address, U256, U256) { + let vm = self.vm(); + ( + vm.msg_sender(), // Caller's address + vm.msg_value(), // ETH sent with call + vm.block_number(), // Current block number + ) + } +} +``` + +See the [Global Variables and Functions](./global-variables-and-functions.mdx) documentation for a complete list of available VM methods. + +## Events + +Events allow contracts to log data to the blockchain, enabling off-chain monitoring and indexing. + +### Defining Events + +Use the `sol!` macro to define events with Solidity-compatible signatures: + +```rust +use alloy_sol_types::sol; +use alloy_primitives::{Address, U256}; + +sol! { + // Up to 3 parameters can be indexed + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + event DataUpdated(string indexed key, bytes data); +} +``` + +**Indexed parameters:** + +- Allow filtering events by that parameter +- Limited to 3 indexed parameters per event +- Indexed parameters are stored in log topics, not data + +### Emitting Events + +Use `self.vm().log()` to emit events: + +```rust +#[public] +impl Token { + pub fn transfer(&mut self, to: Address, value: U256) -> bool { + let from = self.vm().msg_sender(); + + // Transfer logic... + let from_balance = self.balances.get(from); + if from_balance < value { + return false; + } + self.balances.setter(from).set(from_balance - value); + self.balances.setter(to).set(self.balances.get(to) + value); + + // Emit event + self.vm().log(Transfer { from, to, value }); + + true + } +} +``` + +### Raw Log Emission + +For advanced use cases, emit raw logs directly: + +```rust +use alloy_primitives::FixedBytes; + +#[public] +impl Contract { + pub fn emit_raw_log(&self) { + let user = Address::from([0x22; 20]); + let balance = U256::from(1000); + + // Topics (up to 4, must be FixedBytes<32>) + let topics = &[user.into_word()]; + + // Data (arbitrary bytes) + let mut data: Vec = vec![]; + data.extend_from_slice(&balance.to_be_bytes::<32>()); + + self.vm().raw_log(topics, &data).unwrap(); + } +} +``` + +## External Contract Calls + +Stylus contracts can call other contracts (Solidity or Stylus) using typed interfaces or raw calls. + +### Defining Contract Interfaces + +Use `sol_interface!` to define interfaces for external contracts: + +```rust +use stylus_sdk::prelude::*; + +sol_interface! { + interface IToken { + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); + } + + interface IOracle { + function getPrice() external view returns (uint256); + } +} +``` + +### Calling External Contracts + +#### View Calls (Read-Only) + +```rust +use stylus_sdk::call::Call; + +#[public] +impl MyContract { + pub fn get_token_balance(&self, token: IToken, account: Address) -> U256 { + // Call::new() for view calls (no state mutation) + let config = Call::new(); + token.balance_of(self.vm(), config, account).unwrap() + } +} +``` + +#### Mutating Calls + +```rust +#[public] +impl MyContract { + pub fn transfer_tokens(&mut self, token: IToken, to: Address, amount: U256) -> bool { + // Call::new_mutating(self) for state-changing calls + let config = Call::new_mutating(self); + token.transfer(self.vm(), config, to, amount).unwrap() + } +} +``` + +#### Payable Calls + +```rust +#[public] +impl MyContract { + #[payable] + pub fn forward_payment(&mut self, recipient: IPaymentProcessor) -> Result<(), Vec> { + // Forward received ETH to another contract + let value = self.vm().msg_value(); + let config = Call::new_payable(self, value); + + recipient.process_payment(self.vm(), config)?; + Ok(()) + } +} +``` + +#### Configuring Gas + +```rust +#[public] +impl MyContract { + pub fn call_with_limited_gas(&mut self, token: IToken, to: Address) -> bool { + let config = Call::new_mutating(self) + .gas(self.vm().evm_gas_left() / 2); // Use half remaining gas + + token.transfer(self.vm(), config, to, U256::from(100)).unwrap() + } +} +``` + +### Low-Level Calls + +For maximum flexibility, use raw calls: + +```rust +use stylus_sdk::call::{call, static_call, RawCall}; + +#[public] +impl MyContract { + // Low-level call (state-changing) + pub fn execute_call(&mut self, target: Address, calldata: Vec) -> Result, Vec> { + let config = Call::new_mutating(self) + .gas(self.vm().evm_gas_left()); + + call(self.vm(), config, target, &calldata) + } + + // Static call (read-only) + pub fn execute_static_call(&self, target: Address, calldata: Vec) -> Result, Vec> { + static_call(self.vm(), Call::new(), target, &calldata) + } + + // Unsafe raw call with advanced options + pub fn execute_raw_call(&mut self, target: Address, calldata: Vec) -> Result, Vec> { + unsafe { + RawCall::new_delegate(self.vm()) + .gas(2100) + .limit_return_data(0, 32) + .flush_storage_cache() + .call(target, &calldata) + } + } +} +``` + +**Call Types:** + +- `call()`: State-changing call to another contract +- `static_call()`: Read-only call (equivalent to Solidity `staticcall`) +- `RawCall`: Low-level unsafe calls with fine-grained control + +## Error Handling + +Stylus contracts can define and return custom errors using Solidity-compatible error types. + +### Defining Errors + +```rust +use alloy_sol_types::sol; + +sol! { + error Unauthorized(); + error InsufficientBalance(address from, uint256 have, uint256 want); + error InvalidAddress(address addr); +} + +#[derive(SolidityError)] +pub enum TokenError { + Unauthorized(Unauthorized), + InsufficientBalance(InsufficientBalance), + InvalidAddress(InvalidAddress), +} +``` + +### Using Errors in Methods + +```rust +#[public] +impl Token { + pub fn transfer(&mut self, to: Address, amount: U256) -> Result { + let from = self.vm().msg_sender(); + + if to == Address::ZERO { + return Err(TokenError::InvalidAddress(InvalidAddress { addr: to })); + } + + let balance = self.balances.get(from); + if balance < amount { + return Err(TokenError::InsufficientBalance(InsufficientBalance { + from, + have: balance, + want: amount, + })); + } + + self.balances.setter(from).set(balance - amount); + self.balances.setter(to).set(self.balances.get(to) + amount); + + Ok(true) + } +} +``` + +**Error handling notes:** + +- Errors automatically encode as Solidity-compatible error data +- Use `Result` where `E` implements `SolidityError` +- Error data includes the error signature and parameters +- Compatible with Solidity `try/catch` blocks + +## Complete Example + +Here's a complete ERC-20-style token contract demonstrating all major features: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +// Define events +sol! { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +// Define errors +sol! { + error InsufficientBalance(address from, uint256 have, uint256 want); + error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want); + error Unauthorized(); +} + +#[derive(SolidityError)] +pub enum TokenError { + InsufficientBalance(InsufficientBalance), + InsufficientAllowance(InsufficientAllowance), + Unauthorized(Unauthorized), +} + +// Define storage +sol_storage! { + #[entrypoint] + pub struct SimpleToken { + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) allowances; + uint256 total_supply; + address owner; + } +} + +#[public] +impl SimpleToken { + // Constructor + #[constructor] + pub fn constructor(&mut self, initial_supply: U256) { + let deployer = self.vm().msg_sender(); + self.owner.set(deployer); + self.balances.setter(deployer).set(initial_supply); + self.total_supply.set(initial_supply); + + self.vm().log(Transfer { + from: Address::ZERO, + to: deployer, + value: initial_supply, + }); + } + + // View functions + pub fn balance_of(&self, account: Address) -> U256 { + self.balances.get(account) + } + + pub fn allowance(&self, owner: Address, spender: Address) -> U256 { + self.allowances.getter(owner).get(spender) + } + + pub fn total_supply(&self) -> U256 { + self.total_supply.get() + } + + pub fn owner(&self) -> Address { + self.owner.get() + } + + // Write functions + pub fn transfer(&mut self, to: Address, value: U256) -> Result { + let from = self.vm().msg_sender(); + self._transfer(from, to, value)?; + Ok(true) + } + + pub fn approve(&mut self, spender: Address, value: U256) -> bool { + let owner = self.vm().msg_sender(); + self.allowances.setter(owner).setter(spender).set(value); + + self.vm().log(Approval { owner, spender, value }); + true + } + + pub fn transfer_from( + &mut self, + from: Address, + to: Address, + value: U256 + ) -> Result { + let spender = self.vm().msg_sender(); + + // Check allowance + let current_allowance = self.allowances.getter(from).get(spender); + if current_allowance < value { + return Err(TokenError::InsufficientAllowance(InsufficientAllowance { + owner: from, + spender, + have: current_allowance, + want: value, + })); + } + + // Update allowance + self.allowances.setter(from).setter(spender).set(current_allowance - value); + + // Transfer + self._transfer(from, to, value)?; + Ok(true) + } + + // Owner-only functions + pub fn mint(&mut self, to: Address, value: U256) -> Result<(), TokenError> { + if self.vm().msg_sender() != self.owner.get() { + return Err(TokenError::Unauthorized(Unauthorized {})); + } + + self.balances.setter(to).set(self.balances.get(to) + value); + self.total_supply.set(self.total_supply.get() + value); + + self.vm().log(Transfer { + from: Address::ZERO, + to, + value, + }); + + Ok(()) + } + + // Internal helper function + fn _transfer(&mut self, from: Address, to: Address, value: U256) -> Result<(), TokenError> { + let from_balance = self.balances.get(from); + if from_balance < value { + return Err(TokenError::InsufficientBalance(InsufficientBalance { + from, + have: from_balance, + want: value, + })); + } + + self.balances.setter(from).set(from_balance - value); + self.balances.setter(to).set(self.balances.get(to) + value); + + self.vm().log(Transfer { from, to, value }); + Ok(()) + } +} +``` + +## Best Practices + +### 1. Use Appropriate State Mutability + +```rust +// Good: Read-only functions use &self +pub fn get_balance(&self, account: Address) -> U256 { + self.balances.get(account) +} + +// Good: State-changing functions use &mut self +pub fn set_balance(&mut self, account: Address, balance: U256) { + self.balances.setter(account).set(balance); +} +``` + +### 2. Validate Inputs Early + +```rust +pub fn transfer(&mut self, to: Address, amount: U256) -> Result { + // Validate inputs first + if to == Address::ZERO { + return Err(TokenError::InvalidAddress(InvalidAddress { addr: to })); + } + + if amount == U256::ZERO { + return Ok(true); // Nothing to transfer + } + + // Then proceed with logic + let from = self.vm().msg_sender(); + // ... +} +``` + +### 3. Use Custom Errors + +```rust +// Good: Descriptive custom errors +pub fn withdraw(&mut self, amount: U256) -> Result<(), VaultError> { + let balance = self.balances.get(self.vm().msg_sender()); + if balance < amount { + return Err(VaultError::InsufficientBalance(InsufficientBalance { + have: balance, + want: amount, + })); + } + // ... +} + +// Avoid: Generic Vec errors +pub fn withdraw(&mut self, amount: U256) -> Result<(), Vec> { + // Less informative +} +``` + +### 4. Emit Events for State Changes + +```rust +pub fn update_value(&mut self, new_value: U256) { + let old_value = self.value.get(); + self.value.set(new_value); + + // Always emit events for important state changes + self.vm().log(ValueUpdated { + old_value, + new_value, + }); +} +``` + +### 5. Access Control Patterns + +```rust +// Good: Clear access control checks +pub fn admin_function(&mut self) -> Result<(), TokenError> { + if self.vm().msg_sender() != self.owner.get() { + return Err(TokenError::Unauthorized(Unauthorized {})); + } + // Admin logic... + Ok(()) +} + +// Consider: Reusable modifier-like helper +impl Token { + fn only_owner(&self) -> Result<(), TokenError> { + if self.vm().msg_sender() != self.owner.get() { + return Err(TokenError::Unauthorized(Unauthorized {})); + } + Ok(()) + } + + pub fn admin_function(&mut self) -> Result<(), TokenError> { + self.only_owner()?; + // Admin logic... + Ok(()) + } +} +``` + +### 6. Gas-Efficient Storage Access + +```rust +// Good: Read once, use multiple times +pub fn complex_calculation(&self, account: Address) -> U256 { + let balance = self.balances.get(account); // Read once + let result = balance * U256::from(2) + balance / U256::from(10); + result +} + +// Avoid: Multiple reads of same storage slot +pub fn inefficient_calculation(&self, account: Address) -> U256 { + self.balances.get(account) * U256::from(2) + self.balances.get(account) / U256::from(10) +} +``` + +### 7. Check Effects Interactions Pattern + +```rust +// Good: Check-Effects-Interactions pattern +pub fn withdraw(&mut self, amount: U256) -> Result<(), VaultError> { + let caller = self.vm().msg_sender(); + + // Checks + let balance = self.balances.get(caller); + if balance < amount { + return Err(VaultError::InsufficientBalance(InsufficientBalance { + have: balance, + want: amount, + })); + } + + // Effects (update state BEFORE external calls) + self.balances.setter(caller).set(balance - amount); + + // Interactions (external calls last) + // self.transfer_eth(caller, amount)?; + + Ok(()) +} +``` + +### 8. Use Type-Safe Interfaces for External Calls + +```rust +// Good: Use sol_interface! for type safety +sol_interface! { + interface IToken { + function transfer(address to, uint256 amount) external returns (bool); + } +} + +pub fn call_token(&mut self, token: IToken, to: Address, amount: U256) -> bool { + let config = Call::new_mutating(self); + token.transfer(self.vm(), config, to, amount).unwrap() +} + +// Avoid: Raw calls unless necessary +pub fn raw_call(&mut self, token: Address, to: Address, amount: U256) -> Vec { + // Less type-safe, more error-prone + let config = Call::new_mutating(self); + let calldata = /* manually construct */; + call(self.vm(), config, token, &calldata).unwrap() +} +``` + +## Delegate calls + +Delegate calls allow a contract to execute code from another contract while maintaining its own context. When Contract A executes a delegate call to Contract B, B's code runs using Contract A's storage, `msg.sender`, and `msg.value`. This means any state changes affect Contract A, and the original sender and value of the transaction are preserved. + +This pattern is essential for building upgradeable contracts, proxy patterns, and modular smart contract systems. + +### Using the low-level `delegate_call` function + +The `delegate_call` function is a low-level operation similar to `call` and `static_call`. It is considered unsafe because it requires trusting the external contract to maintain safety. + +```rust +pub unsafe fn delegate_call( + context: impl MutatingCallContext, + to: Address, + data: &[u8], +) -> Result, Error> +``` + +**Example usage:** + +```rust +use stylus_sdk::call::delegate_call; + +pub fn low_level_delegate_call( + &mut self, + calldata: Vec, + target: Address, +) -> Result, DelegateCallErrors> { + unsafe { + let result = delegate_call(self, target, &calldata) + .map_err(|_| DelegateCallErrors::DelegateCallFailed(DelegateCallFailed {}))?; + Ok(result) + } +} +``` + +### Using `RawCall` with `new_delegate()` + +For scenarios requiring untyped calls with more configuration options, `RawCall` offers a fluent interface. You can set up a delegate call by chaining optional configuration methods. + +```rust +use stylus_sdk::call::RawCall; + +pub fn raw_delegate_call( + &mut self, + calldata: Vec, + target: Address, +) -> Result, Vec> { + let data = RawCall::new_delegate() // Configure a delegate call + .gas(2100) // Supply 2100 gas + .limit_return_data(0, 32) // Only read the first 32 bytes back + .call(target, &calldata)?; + + Ok(data) +} +``` + +### Safety considerations + +:::caution +Delegate calls are inherently unsafe and require careful consideration before use. +::: + +- **Trust requirement**: The calling contract must trust the external contract to uphold safety requirements +- **Storage modification**: The external contract can arbitrarily change the calling contract's storage +- **Ether spending**: The external contract may spend ether or perform other critical operations on behalf of the caller +- **Cache clearing**: While the `delegate_call` function clears any cached values, it cannot prevent unsafe actions by the external contract + +### Complete delegate call example + +```rust +#![cfg_attr(not(feature = "export-abi"), no_main)] +extern crate alloc; + +use alloy_sol_types::sol; +use stylus_sdk::{ + alloy_primitives::Address, + call::{delegate_call, RawCall}, + prelude::*, +}; + +#[storage] +#[entrypoint] +pub struct DelegateExample; + +sol! { + error DelegateCallFailed(); +} + +#[derive(SolidityError)] +pub enum DelegateCallErrors { + DelegateCallFailed(DelegateCallFailed), +} + +#[public] +impl DelegateExample { + // Low-level delegate call + pub fn low_level_delegate_call( + &mut self, + calldata: Vec, + target: Address, + ) -> Result, DelegateCallErrors> { + unsafe { + let result = delegate_call(self, target, &calldata) + .map_err(|_| DelegateCallErrors::DelegateCallFailed(DelegateCallFailed {}))?; + + Ok(result) + } + } + + // RawCall delegate call with configuration + pub fn raw_delegate_call( + &mut self, + calldata: Vec, + target: Address, + ) -> Result, Vec> { + let data = RawCall::new_delegate() + .gas(2100) + .limit_return_data(0, 32) + .call(target, &calldata)?; + + Ok(data) + } +} +``` + +## Sending ether + +Stylus provides multiple ways to send ether from a contract. Unlike Solidity's `transfer` method which is capped at 2300 gas, Stylus's `transfer_eth` forwards all gas to the recipient. You can cap gas using the low-level `call` method if needed. + +### Methods for sending ether + +| Method | Description | Gas behavior | +| ------------------------ | --------------------------------------- | ------------------------------- | +| `transfer_eth()` | Simple ether transfer | Forwards all gas | +| `call()` with `.value()` | Low-level call with value | Forwards all gas (configurable) | +| Payable external calls | Call payable methods on other contracts | Forwards all gas (configurable) | + +### Using `transfer_eth()` + +The simplest way to send ether: + +```rust +use stylus_sdk::call::transfer_eth; + +#[public] +impl SendEther { + #[payable] + pub fn send_via_transfer(to: Address) -> Result<(), Vec> { + transfer_eth(to, msg::value())?; + Ok(()) + } +} +``` + +### Using low-level `call()` with value + +For more control over the transfer: + +```rust +use stylus_sdk::call::{call, Call}; + +#[public] +impl SendEther { + #[payable] + pub fn send_via_call(&mut self, to: Address) -> Result<(), Vec> { + call(Call::new_in(self).value(msg::value()), to, &[])?; + Ok(()) + } +} +``` + +These two approaches are equivalent under the hood: + +```rust +// These are equivalent: +transfer_eth(recipient, value)?; +call(Call::new_in(self).value(value), recipient, &[])?; +``` + +### Sending with a gas limit + +To cap the gas forwarded to the recipient (similar to Solidity's `transfer`): + +```rust +#[payable] +pub fn send_via_call_gas_limit(&mut self, to: Address, gas_amount: u64) -> Result<(), Vec> { + call( + Call::new_in(self).value(msg::value()).gas(gas_amount), + to, + &[], + )?; + Ok(()) +} +``` + +### Sending with calldata + +To trigger a fallback function on the receiving contract: + +```rust +use stylus_sdk::abi::Bytes; + +#[payable] +pub fn send_via_call_with_calldata( + &mut self, + to: Address, + data: Bytes, +) -> Result<(), Vec> { + call(Call::new_in(self).value(msg::value()), to, data.as_slice())?; + Ok(()) +} +``` + +### Sending to payable contract methods + +Use typed interfaces to call payable methods on other contracts: + +```rust +sol_interface! { + interface ITarget { + function receiveEther() external payable; + } +} + +#[public] +impl SendEther { + #[payable] + pub fn send_to_contract(&mut self, to: Address) -> Result<(), Vec> { + let target = ITarget::new(to); + let config = Call::new_in(self).value(msg::value()); + target.receive_ether(config)?; + Ok(()) + } +} +``` + +### Where you can send ether + +1. **Externally owned account (EOA) addresses**: Directly send ether to any EOA address +2. **Solidity contracts with `receive()` function**: Send ether without calldata to contracts implementing `receive()` +3. **Solidity contracts with `fallback()` function**: Send ether with calldata to contracts implementing `fallback()` +4. **Contracts with payable methods**: Call any payable method on Solidity or Stylus contracts + +### Complete sending ether example + +```rust +#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] +extern crate alloc; + +use alloy_primitives::Address; +use stylus_sdk::{ + abi::Bytes, + call::{call, transfer_eth, Call}, + msg, + prelude::*, +}; + +sol_interface! { + interface ITarget { + function receiveEther() external payable; + } +} + +#[storage] +#[entrypoint] +pub struct SendEther; + +#[public] +impl SendEther { + // Simple transfer + #[payable] + pub fn send_via_transfer(to: Address) -> Result<(), Vec> { + transfer_eth(to, msg::value())?; + Ok(()) + } + + // Low-level call + #[payable] + pub fn send_via_call(&mut self, to: Address) -> Result<(), Vec> { + call(Call::new_in(self).value(msg::value()), to, &[])?; + Ok(()) + } + + // With gas limit + #[payable] + pub fn send_via_call_gas_limit(&mut self, to: Address, gas_amount: u64) -> Result<(), Vec> { + call( + Call::new_in(self).value(msg::value()).gas(gas_amount), + to, + &[], + )?; + Ok(()) + } + + // With calldata (triggers fallback) + #[payable] + pub fn send_via_call_with_calldata( + &mut self, + to: Address, + data: Bytes, + ) -> Result<(), Vec> { + call(Call::new_in(self).value(msg::value()), to, data.as_slice())?; + Ok(()) + } + + // To payable contract method + #[payable] + pub fn send_to_contract(&mut self, to: Address) -> Result<(), Vec> { + let target = ITarget::new(to); + let config = Call::new_in(self).value(msg::value()); + target.receive_ether(config)?; + Ok(()) + } +} +``` + +## Factory contract deployment (coming soon) + +The factory pattern allows a contract to deploy other contracts programmatically. This is useful for creating contract instances on-demand, such as deploying new token contracts or creating user-specific vaults. + +:::note +Advanced deployment patterns documentation is in development. This section will cover: + +- Deploying contracts from within a contract +- Passing constructor arguments +- Deterministic deployment with CREATE2 +- Handling deployment failures + ::: + +**Constructor considerations for factory-deployed contracts:** + +When a contract is deployed via a factory contract (rather than directly by an EOA), the `msg_sender()` in the constructor will be the factory contract's address, not the original deployer. If you need the original deployer's address, use `tx_origin()` instead: + +```rust +#[public] +impl FactoryDeployedContract { + #[constructor] + pub fn constructor(&mut self) { + // msg_sender() = factory contract address + // tx_origin() = original transaction sender (EOA) + let original_deployer = self.vm().tx_origin(); + self.owner.set(original_deployer); + } +} +``` + +## Function modifiers and access control patterns + +Unlike Solidity, Rust does not have built-in modifier syntax. However, you can achieve similar functionality using helper functions that return `Result<(), Error>` combined with the `?` operator. + +### Basic modifier pattern + +Create helper functions that perform checks and return early on failure: + +```rust +sol! { + error Unauthorized(); + error Paused(); +} + +#[derive(SolidityError)] +pub enum ContractError { + Unauthorized(Unauthorized), + Paused(Paused), +} + +#[public] +impl MyContract { + // Modifier-like helper function + fn only_owner(&self) -> Result<(), ContractError> { + if self.vm().msg_sender() != self.owner.get() { + return Err(ContractError::Unauthorized(Unauthorized {})); + } + Ok(()) + } + + // Using the "modifier" with the ? operator + pub fn admin_function(&mut self) -> Result<(), ContractError> { + self.only_owner()?; // Returns early if check fails + + // Admin logic here... + Ok(()) + } +} +``` + +### Multiple guard functions + +You can combine multiple checks by chaining helper functions: + +```rust +#[public] +impl MyContract { + fn only_owner(&self) -> Result<(), ContractError> { + if self.vm().msg_sender() != self.owner.get() { + return Err(ContractError::Unauthorized(Unauthorized {})); + } + Ok(()) + } + + fn when_not_paused(&self) -> Result<(), ContractError> { + if self.paused.get() { + return Err(ContractError::Paused(Paused {})); + } + Ok(()) + } + + fn only_after(&self, timestamp: u64) -> Result<(), ContractError> { + if self.vm().block_timestamp() < timestamp { + return Err(ContractError::TooEarly(TooEarly {})); + } + Ok(()) + } + + // Combining multiple "modifiers" + pub fn protected_action(&mut self) -> Result<(), ContractError> { + self.only_owner()?; + self.when_not_paused()?; + self.only_after(self.unlock_time.get())?; + + // Protected logic here... + Ok(()) + } +} +``` + +### Reusable access control module + +For larger projects, encapsulate access control in a reusable module: + +```rust +// Access control helpers +impl MyContract { + fn require_role(&self, role: FixedBytes<32>, account: Address) -> Result<(), ContractError> { + if !self.has_role(role, account) { + return Err(ContractError::MissingRole(MissingRole { role, account })); + } + Ok(()) + } + + fn has_role(&self, role: FixedBytes<32>, account: Address) -> bool { + self.roles.getter(role).get(account) + } + + // Grant role (admin only) + pub fn grant_role( + &mut self, + role: FixedBytes<32>, + account: Address, + ) -> Result<(), ContractError> { + self.require_role(self.admin_role(), self.vm().msg_sender())?; + self.roles.setter(role).setter(account).set(true); + Ok(()) + } +} +``` + +### Complete access control example + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + error Unauthorized(); + error Paused(); + error InvalidAmount(); +} + +#[derive(SolidityError)] +pub enum VaultError { + Unauthorized(Unauthorized), + Paused(Paused), + InvalidAmount(InvalidAmount), +} + +sol_storage! { + #[entrypoint] + pub struct Vault { + address owner; + bool paused; + mapping(address => uint256) balances; + } +} + +#[public] +impl Vault { + // Modifier-like helpers + fn only_owner(&self) -> Result<(), VaultError> { + if self.vm().msg_sender() != self.owner.get() { + return Err(VaultError::Unauthorized(Unauthorized {})); + } + Ok(()) + } + + fn when_not_paused(&self) -> Result<(), VaultError> { + if self.paused.get() { + return Err(VaultError::Paused(Paused {})); + } + Ok(()) + } + + fn valid_amount(&self, amount: U256) -> Result<(), VaultError> { + if amount == U256::ZERO { + return Err(VaultError::InvalidAmount(InvalidAmount {})); + } + Ok(()) + } + + // Public functions using modifiers + #[payable] + pub fn deposit(&mut self) -> Result<(), VaultError> { + self.when_not_paused()?; + + let sender = self.vm().msg_sender(); + let amount = self.vm().msg_value(); + self.valid_amount(amount)?; + + let current = self.balances.get(sender); + self.balances.setter(sender).set(current + amount); + Ok(()) + } + + pub fn pause(&mut self) -> Result<(), VaultError> { + self.only_owner()?; + self.paused.set(true); + Ok(()) + } + + pub fn unpause(&mut self) -> Result<(), VaultError> { + self.only_owner()?; + self.paused.set(false); + Ok(()) + } +} +``` + +## See Also + +- [Primitives](./data-types/primitives.mdx) - Basic data types +- [Compound Types](./data-types/compound-types.mdx) - Arrays, structs, tuples +- [Storage Types](./data-types/storage.mdx) - Persistent storage +- [Global Variables and Functions](./global-variables-and-functions.mdx) - VM context methods diff --git a/docs/stylus/fundamentals/data-types/compound-types.mdx b/docs/stylus/fundamentals/data-types/compound-types.mdx new file mode 100644 index 0000000000..6cef7b8c79 --- /dev/null +++ b/docs/stylus/fundamentals/data-types/compound-types.mdx @@ -0,0 +1,845 @@ +--- +title: 'Stylus compound types' +description: 'Stylus Rust SDK compound types' +author: chrisco +sme: chrisco +sidebar_position: 2 +target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +displayed_sidebar: buildStylusSidebar +--- + +Compound types allow you to group multiple values together in Stylus contracts. The SDK provides full support for tuples, structs, arrays, and vectors with automatic ABI encoding/decoding and Solidity type mappings. + +## Tuples + +Tuples group multiple values of different types together. They map directly to Solidity tuples. + +### Basic tuples + +```rust +use alloy_primitives::{Address, U256, Bytes}; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // Return multiple values as a tuple + pub fn get_data(&self) -> (U256, Address, bool) { + (U256::from(100), Address::ZERO, true) + } + + // Accept tuple as parameter + pub fn process_tuple(&mut self, data: (U256, U256, U256)) -> U256 { + let (a, b, c) = data; + a + b + c + } + + // Nested tuples + pub fn nested(&self) -> ((U256, U256), bool) { + ((U256::from(1), U256::from(2)), true) + } +} +``` + +### Tuple destructuring + +```rust +use alloy_primitives::U256; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + pub fn calculate(&self) -> (U256, U256) { + let values = (U256::from(100), U256::from(200)); + + // Destructure the tuple + let (first, second) = values; + + // Return new tuple + (first * U256::from(2), second * U256::from(2)) + } + + // Pattern matching with tuples + pub fn match_tuple(&self, data: (bool, U256)) -> U256 { + match data { + (true, value) => value * U256::from(2), + (false, value) => value, + } + } +} +``` + +### Tuple type mappings + +| Rust Type | Solidity Type | ABI Signature | +| ---------------------- | ---------------------------- | ---------------------------- | +| `(U256,)` | `(uint256)` | `"(uint256)"` | +| `(U256, Address)` | `(uint256, address)` | `"(uint256,address)"` | +| `(bool, U256, Bytes)` | `(bool, uint256, bytes)` | `"(bool,uint256,bytes)"` | +| `((U256, U256), bool)` | `((uint256, uint256), bool)` | `"((uint256,uint256),bool)"` | + +**Tuple Limitations**: + +- Tuples support up to 24 elements +- Tuples are always returned as `memory` in Solidity +- Empty tuple `()` represents no return value + +## Structs + +Structs define custom data types with named fields. Use the `sol!` macro to define Solidity-compatible structs. + +### Defining structs with `sol!` + +```rust +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + #[derive(Debug, AbiType)] + struct User { + address account; + uint256 balance; + string name; + } + + #[derive(Debug, AbiType)] + struct Token { + string name; + string symbol; + uint8 decimals; + } +} + +#[public] +impl MyContract { + pub fn get_user(&self) -> User { + User { + account: Address::ZERO, + balance: U256::from(1000), + name: "Alice".to_string(), + } + } + + pub fn process_user(&mut self, user: User) -> U256 { + // Access struct fields + user.balance + } + + pub fn get_token_info(&self) -> Token { + Token { + name: "MyToken".to_string(), + symbol: "MTK".to_string(), + decimals: 18, + } + } +} +``` + +### Nested structs + +Structs can contain other structs, enabling complex data structures: + +```rust +use alloy_primitives::Address; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + #[derive(Debug, AbiType)] + struct Dog { + string name; + string breed; + } + + #[derive(Debug, AbiType)] + struct User { + address account; + string name; + Dog[] dogs; + } +} + +#[public] +impl MyContract { + pub fn create_user(&self) -> User { + let dogs = vec![ + Dog { + name: "Rex".to_string(), + breed: "Labrador".to_string(), + }, + Dog { + name: "Max".to_string(), + breed: "Beagle".to_string(), + }, + ]; + + User { + account: Address::ZERO, + name: "Alice".to_string(), + dogs, + } + } + + pub fn get_dog_count(&self, user: User) -> u256 { + user.dogs.len() as u256 + } +} +``` + +### Struct best practices + +1. **Always use `#[derive(AbiType)]`** for structs that will be used in contract interfaces: + + ```rust + sol! { + #[derive(Debug, AbiType)] + struct MyData { + uint256 value; + address owner; + } + } + ``` + +2. **Add `Debug` derive** for easier debugging: + + ```rust + sol! { + #[derive(Debug, AbiType)] + struct Config { + bool enabled; + uint256 timeout; + } + } + ``` + +3. **Use descriptive field names** that match Solidity conventions: + ```rust + sol! { + #[derive(Debug, AbiType)] + struct VestingSchedule { + address beneficiary; + uint256 startTime; + uint256 cliffDuration; + uint256 totalAmount; + } + } + ``` + +## Arrays + +Arrays are fixed-size collections of elements. Stylus supports both Rust arrays and Solidity-style arrays. + +### Fixed-size arrays + +```rust +use alloy_primitives::U256; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // Return a fixed-size array + pub fn get_numbers(&self) -> [U256; 5] { + [ + U256::from(1), + U256::from(2), + U256::from(3), + U256::from(4), + U256::from(5), + ] + } + + // Accept fixed-size array as parameter + pub fn sum_array(&self, numbers: [U256; 5]) -> U256 { + numbers.iter().fold(U256::ZERO, |acc, &x| acc + x) + } + + // Nested arrays + pub fn matrix(&self) -> [[u32; 2]; 3] { + [[1, 2], [3, 4], [5, 6]] + } +} +``` + +### Array operations + +```rust +use alloy_primitives::{Address, U256}; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // Iterate over array + pub fn process_addresses(&self, addresses: [Address; 10]) -> U256 { + let mut count = U256::ZERO; + for addr in addresses.iter() { + if *addr != Address::ZERO { + count += U256::from(1); + } + } + count + } + + // Array of booleans + pub fn check_flags(&self, flags: [bool; 8]) -> bool { + flags.iter().all(|&f| f) + } +} +``` + +### Array type mappings + +| Rust Type | Solidity Type | Description | +| --------------------- | -------------- | ------------------------- | +| `[U256; 5]` | `uint256[5]` | 5-element uint256 array | +| `[bool; 10]` | `bool[10]` | 10-element bool array | +| `[Address; 3]` | `address[3]` | 3-element address array | +| `[[u32; 2]; 4]` | `uint32[2][4]` | Nested array (4x2 matrix) | +| `[FixedBytes<32>; 2]` | `bytes32[2]` | 2-element bytes32 array | + +## Vectors + +Vectors are dynamic arrays that can grow or shrink at runtime. They map to Solidity dynamic arrays. + +### Basic vector usage + +```rust +use alloy_primitives::{Address, U256, Bytes}; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // Return a vector + pub fn get_numbers(&self) -> Vec { + vec![U256::from(1), U256::from(2), U256::from(3)] + } + + // Accept vector as parameter + pub fn sum_vec(&self, numbers: Vec) -> U256 { + numbers.iter().fold(U256::ZERO, |acc, x| acc + *x) + } + + // Vector of addresses + pub fn get_addresses(&self) -> Vec
{ + vec![Address::ZERO, Address::ZERO] + } + + // Vector of bytes + pub fn get_data_list(&self) -> Vec { + vec![ + Bytes::from(vec![1, 2, 3]), + Bytes::from(vec![4, 5, 6]), + ] + } +} +``` + +### Vector operations + +```rust +use alloy_primitives::U256; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // Filter vector + pub fn filter_even(&self, numbers: Vec) -> Vec { + numbers + .into_iter() + .filter(|n| n.byte(0) % 2 == 0) + .collect() + } + + // Map over vector + pub fn double_values(&self, numbers: Vec) -> Vec { + numbers + .into_iter() + .map(|n| n * U256::from(2)) + .collect() + } + + // Find in vector + pub fn contains_value(&self, numbers: Vec, target: U256) -> bool { + numbers.contains(&target) + } + + // Get vector length + pub fn get_length(&self, items: Vec) -> U256 { + U256::from(items.len()) + } +} +``` + +### Vectors of structs + +```rust +use alloy_primitives::Address; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + #[derive(Debug, AbiType)] + struct Transaction { + address from; + address to; + uint256 amount; + } +} + +#[public] +impl MyContract { + pub fn get_transactions(&self) -> Vec { + vec![ + Transaction { + from: Address::ZERO, + to: Address::ZERO, + amount: U256::from(100), + }, + Transaction { + from: Address::ZERO, + to: Address::ZERO, + amount: U256::from(200), + }, + ] + } + + pub fn total_amount(&self, txs: Vec) -> U256 { + txs.iter() + .fold(U256::ZERO, |acc, tx| acc + tx.amount) + } +} +``` + +### Vector type mappings + +| Rust Type | Solidity Type | ABI Signature | Storage | +| --------------- | ------------- | --------------------- | ------- | +| `Vec` | `uint256[]` | `"uint256[] memory"` | Dynamic | +| `Vec
` | `address[]` | `"address[] memory"` | Dynamic | +| `Vec` | `bool[]` | `"bool[] memory"` | Dynamic | +| `Vec` | `bytes[]` | `"bytes[] memory"` | Dynamic | +| `Vec` | `MyStruct[]` | `"MyStruct[] memory"` | Dynamic | + +**Important Notes**: + +- Vectors are **always returned as `memory`** in Solidity, never as `calldata` +- `Vec` maps to `uint8[]`, not `bytes` (use `Bytes` for Solidity `bytes`) +- Vectors have dynamic size and consume more gas than fixed arrays + +## Bytes Types + +The SDK provides `Bytes` for dynamic byte arrays and `FixedBytes` for fixed-size byte arrays. + +### Dynamic bytes (`Bytes`) + +```rust +use alloy_primitives::Bytes; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // Return dynamic bytes + pub fn get_data(&self) -> Bytes { + Bytes::from(vec![1, 2, 3, 4, 5]) + } + + // Process bytes + pub fn get_length(&self, data: Bytes) -> usize { + data.len() + } + + // Concatenate bytes + pub fn concat(&self, a: Bytes, b: Bytes) -> Bytes { + let mut result = a.to_vec(); + result.extend_from_slice(&b); + Bytes::from(result) + } +} +``` + +### Fixed bytes (`FixedBytes`) + +```rust +use alloy_primitives::FixedBytes; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // bytes32 (common for hashes) + pub fn get_hash(&self) -> FixedBytes<32> { + FixedBytes::<32>::ZERO + } + + // bytes4 (common for selectors) + pub fn get_selector(&self) -> FixedBytes<4> { + FixedBytes::from([0x12, 0x34, 0x56, 0x78]) + } + + // bytes16 + pub fn get_uuid(&self) -> FixedBytes<16> { + FixedBytes::<16>::from([ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, + ]) + } +} +``` + +## Complete examples + +### Example 1: Complex data structures + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloc::string::String; +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + #[derive(Debug, AbiType)] + struct Token { + string name; + string symbol; + uint8 decimals; + uint256 totalSupply; + } + + #[derive(Debug, AbiType)] + struct Balance { + address owner; + uint256 amount; + } +} + +sol_storage! { + #[entrypoint] + pub struct CompoundExample { + uint256 counter; + } +} + +#[public] +impl CompoundExample { + // Return tuple + pub fn get_info(&self) -> (String, U256, bool) { + ("Example".to_string(), U256::from(42), true) + } + + // Return struct + pub fn get_token(&self) -> Token { + Token { + name: "MyToken".to_string(), + symbol: "MTK".to_string(), + decimals: 18, + totalSupply: U256::from(1000000), + } + } + + // Return vector of structs + pub fn get_balances(&self) -> Vec { + vec![ + Balance { + owner: Address::ZERO, + amount: U256::from(100), + }, + Balance { + owner: Address::ZERO, + amount: U256::from(200), + }, + ] + } + + // Accept array + pub fn process_array(&self, data: [U256; 5]) -> U256 { + data.iter().sum() + } + + // Accept vector and struct + pub fn batch_transfer(&mut self, recipients: Vec) -> U256 { + recipients.iter().map(|b| b.amount).sum() + } +} +``` + +### Example 2: Nested data structures + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloc::string::String; +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + #[derive(Debug, AbiType)] + struct Dog { + string name; + string breed; + } + + #[derive(Debug, AbiType)] + struct User { + address account; + string name; + Dog[] dogs; + } +} + +sol_storage! { + #[entrypoint] + pub struct NestedExample {} +} + +#[public] +impl NestedExample { + pub fn create_user(&self) -> User { + User { + account: Address::ZERO, + name: "Alice".to_string(), + dogs: vec![ + Dog { + name: "Rex".to_string(), + breed: "Labrador".to_string(), + }, + Dog { + name: "Max".to_string(), + breed: "Beagle".to_string(), + }, + ], + } + } + + pub fn get_dog_names(&self, user: User) -> Vec { + user.dogs.into_iter().map(|dog| dog.name).collect() + } + + pub fn count_dogs(&self, users: Vec) -> U256 { + let total: usize = users.iter().map(|u| u.dogs.len()).sum(); + U256::from(total) + } +} +``` + +## Best practices + +### 1. Choose the right type + +```rust +// Use tuples for simple groupings +pub fn get_basics(&self) -> (U256, Address, bool) { /* ... */ } + +// Use structs for complex data with named fields +sol! { + #[derive(Debug, AbiType)] + struct UserProfile { + address account; + string name; + uint256 balance; + bool active; + } +} + +// Use arrays for fixed-size collections +pub fn get_top_five(&self) -> [U256; 5] { /* ... */ } + +// Use vectors for dynamic collections +pub fn get_all_users(&self) -> Vec
{ /* ... */ } +``` + +### 2. Memory efficiency + +```rust +use alloy_primitives::U256; + +// Prefer fixed arrays when size is known +pub fn fixed_data(&self) -> [U256; 10] { + // More gas-efficient + [U256::ZERO; 10] +} + +// Use vectors only when size varies +pub fn dynamic_data(&self, count: usize) -> Vec { + vec![U256::ZERO; count] +} +``` + +### 3. Struct naming + +```rust +use alloy_sol_types::sol; + +sol! { + // Good: Clear, descriptive names + #[derive(Debug, AbiType)] + struct TokenMetadata { + string name; + string symbol; + uint8 decimals; + } + + // Avoid: Ambiguous names + #[derive(Debug, AbiType)] + struct Data { + uint256 x; + uint256 y; + } +} +``` + +### 4. Vector vs array + +```rust +use alloy_primitives::{Address, U256}; + +// Use fixed arrays for known sizes +pub fn get_admins(&self) -> [Address; 3] { + // Three admin addresses + [Address::ZERO; 3] +} + +// Use vectors for variable sizes +pub fn get_users(&self) -> Vec
{ + // Unknown number of users + vec![] +} +``` + +### 5. Nested structures + +```rust +use alloy_sol_types::sol; + +sol! { + // Good: Reasonable nesting depth + #[derive(Debug, AbiType)] + struct User { + address account; + Profile profile; + } + + #[derive(Debug, AbiType)] + struct Profile { + string name; + uint256 age; + } + + // Avoid: Excessive nesting (gas inefficient) + #[derive(Debug, AbiType)] + struct DeepNesting { + Level1 l1; + } + + #[derive(Debug, AbiType)] + struct Level1 { + Level2 l2; + } + + #[derive(Debug, AbiType)] + struct Level2 { + Level3 l3; + } + + #[derive(Debug, AbiType)] + struct Level3 { + uint256 value; + } +} +``` + +## Type conversion and helpers + +### Converting between types + +```rust +use alloy_primitives::{U256, Bytes}; + +// Vec to Bytes +let vec: Vec = vec![1, 2, 3]; +let bytes = Bytes::from(vec); + +// Bytes to Vec +let bytes = Bytes::from(vec![1, 2, 3]); +let vec: Vec = bytes.to_vec(); + +// Array to Vec +let arr: [U256; 3] = [U256::from(1), U256::from(2), U256::from(3)]; +let vec: Vec = arr.to_vec(); + +// Vec to array (if size matches) +let vec = vec![U256::from(1), U256::from(2), U256::from(3)]; +let arr: [U256; 3] = vec.try_into().unwrap(); +``` + +### Working with iterators + +```rust +use alloy_primitives::U256; + +// Map over vector +let numbers = vec![U256::from(1), U256::from(2), U256::from(3)]; +let doubled: Vec = numbers.iter().map(|n| n * U256::from(2)).collect(); + +// Filter vector +let evens: Vec = numbers.into_iter().filter(|n| n.byte(0) % 2 == 0).collect(); + +// Fold/reduce +let sum = numbers.iter().fold(U256::ZERO, |acc, n| acc + n); +``` + +## Common patterns + +### Batch operations + +```rust +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + #[derive(Debug, AbiType)] + struct Transfer { + address to; + uint256 amount; + } +} + +#[public] +impl MyContract { + pub fn batch_transfer(&mut self, transfers: Vec) -> U256 { + let mut total = U256::ZERO; + for transfer in transfers { + // Process each transfer + total += transfer.amount; + } + total + } +} +``` + +### Pagination + +```rust +use alloy_primitives::U256; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + pub fn get_page(&self, items: Vec, page: usize, size: usize) -> Vec { + let start = page * size; + let end = start + size; + items.get(start..end.min(items.len())) + .unwrap_or(&[]) + .to_vec() + } +} +``` + +## See also + +- [Primitives](./primitives.mdx) - Basic types (bool, integers, address, strings) +- [Storage Types](./storage.mdx) - Persistent storage for compound types +- [Type Conversions](./conversions-between-types.mdx) - Converting between types diff --git a/docs/stylus/fundamentals/data-types/conversions-between-types.mdx b/docs/stylus/fundamentals/data-types/conversions-between-types.mdx new file mode 100644 index 0000000000..d86fd64494 --- /dev/null +++ b/docs/stylus/fundamentals/data-types/conversions-between-types.mdx @@ -0,0 +1,377 @@ +--- +title: 'Conversions between types' +description: 'Learn how to convert between Rust types, Alloy primitives, and Solidity types in Stylus smart contracts' +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +displayed_sidebar: buildStylusSidebar +--- + +Stylus smart contracts often need to convert between different type representations: Rust native types, Alloy primitives, and Solidity types. The Stylus SDK provides comprehensive conversion mechanisms through the `AbiType` trait and various standard Rust conversion traits. + +## Understanding type relationships + +The Stylus SDK establishes a bidirectional relationship between Rust and Solidity types: + +- **Alloy provides**: Solidity types → Rust types mapping via `SolType` +- **Stylus SDK provides**: Rust types → Solidity types mapping via `AbiType` + +Together, these create a complete two-way type system for interoperability. + +### Key type mappings + +| Rust/Alloy type | Solidity type | Notes | +| --------------------------------- | ------------------------- | ---------------------------------------------- | +| `bool` | `bool` | Native boolean | +| `u8`, `u16`, `u32`, `u64`, `u128` | `uint8` through `uint128` | Native unsigned integers | +| `i8`, `i16`, `i32`, `i64`, `i128` | `int8` through `int128` | Native signed integers | +| `Uint` | `uintBITS` | Arbitrary-sized unsigned integers (8-256 bits) | +| `Signed` | `intBITS` | Arbitrary-sized signed integers (8-256 bits) | +| `U256` | `uint256` | 256-bit unsigned integer (most common) | +| `Address` | `address` | 20-byte Ethereum address | +| `FixedBytes` | `bytesN` | Fixed-size byte array | +| `Bytes` | `bytes` | Dynamic byte array | +| `String` | `string` | Dynamic UTF-8 string | +| `Vec` | `T[]` | Dynamic array | +| `[T; N]` | `T[N]` | Fixed-size array | +| `(T1, T2, ...)` | `(T1, T2, ...)` | Tuple types | + +**Important**: The SDK treats `Vec` as Solidity `uint8[]`. For Solidity `bytes`, use `alloy_primitives::Bytes`. + +## Converting numeric types + +### Creating integers from literals + +```rust +use stylus_sdk::alloy_primitives::{U256, I256, U8, I8}; + +// From integer literals +let small: U8 = U8::from(1); +let large: U256 = U256::from(255); + +// For signed integers +let positive: I8 = I8::unchecked_from(127); +let negative: I8 = I8::unchecked_from(-1); +let signed_large: I256 = I256::unchecked_from(0xff_u64); +``` + +### Parsing from strings + +```rust +use stylus_sdk::alloy_primitives::I256; + +// Parse decimal strings +let a = I256::try_from(20003000).unwrap(); +let b = "100".parse::().unwrap(); + +// Parse hexadecimal strings +let c = "-0x138f".parse::().unwrap(); + +// Underscores are ignored for readability +let d = "1_000_000".parse::().unwrap(); + +// Arithmetic works as expected +let result = a * b + c - d; +``` + +### Integer constants + +```rust +use stylus_sdk::alloy_primitives::I256; + +let max = I256::MAX; // Maximum value +let min = I256::MIN; // Minimum value +let zero = I256::ZERO; // Zero +let minus_one = I256::MINUS_ONE; // -1 +``` + +### Converting between integer sizes + +```rust +use stylus_sdk::alloy_primitives::{Uint, Signed, U256}; + +// Between Alloy integer types (same bit-width) +let uint_value = Uint::<128, 2>::from(999); +let u128_value: u128 = uint_value.try_into() + .map_err(|_| "conversion error") + .unwrap(); + +// Between different bit-widths +let small = Uint::<8, 1>::from(100); +let large = U256::from(small); +``` + +The SDK uses the `ConvertInt` trait internally to enable conversions between Alloy's `Uint` types and Rust native integer types like `u8`, `u16`, `u32`, `u64`, and `u128`. + +## Converting addresses + +### Creating addresses + +```rust +use stylus_sdk::alloy_primitives::{Address, address}; + +// From a 20-byte array +let addr1 = Address::from([0x11; 20]); + +// Using the address! macro with checksummed string +let addr2 = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"); + +// From a byte slice +let bytes: [u8; 20] = [0xd8, 0xda, 0x6b, 0xf2, /* ... */]; +let addr3 = Address::from(bytes); +``` + +### Converting addresses to bytes + +```rust +use stylus_sdk::alloy_primitives::Address; + +let addr = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"); + +// Get reference to underlying bytes +let bytes_ref: &[u8] = addr.as_ref(); + +// Use in byte concatenation +let data = [addr.as_ref(), other_data].concat(); +``` + +## Converting byte types + +### Fixed-size bytes + +```rust +use stylus_sdk::alloy_primitives::FixedBytes; + +// Create from array +let fixed = FixedBytes::<32>::new([0u8; 32]); + +// Create from slice +let slice: &[u8] = &[1, 2, 3, 4]; +let fixed = FixedBytes::<4>::from_slice(slice); + +// Convert to slice +let bytes_ref: &[u8] = fixed.as_ref(); +``` + +### Dynamic bytes + +```rust +use stylus_sdk::abi::Bytes; + +// Create from Vec +let bytes = Bytes::from(vec![1, 2, 3, 4]); + +// Create empty +let empty = Bytes::new(); + +// Get reference to underlying data +let data: &[u8] = bytes.as_ref(); + +// Convert to Vec +let vec: Vec = bytes.to_vec(); +``` + +### Byte array conversions + +```rust +use stylus_sdk::alloy_primitives::U256; + +// Convert U256 to big-endian bytes +let value = U256::from(12345); +let bytes_vec: Vec = value.to_be_bytes_vec(); +let bytes_array: [u8; 32] = value.to_be_bytes(); + +// Convert from big-endian bytes +let from_slice = U256::try_from_be_slice(&bytes_vec).unwrap(); +``` + +## Converting strings + +```rust +use alloc::string::{String, ToString}; + +// String conversions +let rust_string = "hello".to_string(); +let bytes = rust_string.as_bytes(); + +// For Solidity string parameters in functions +// the String type is automatically handled by AbiType +pub fn process_string(&self, text: String) -> String { + text +} +``` + +## Converting collections + +### Dynamic arrays (Vec) + +```rust +use stylus_sdk::alloy_primitives::U256; +use alloc::vec::Vec; + +// Vec is used directly as Solidity dynamic arrays +let numbers: Vec = vec![ + U256::from(1), + U256::from(2), + U256::from(3), +]; + +// For Vec, note this maps to uint8[], not bytes +let uint8_array: Vec = vec![1, 2, 3]; +``` + +### Fixed-size arrays + +```rust +use stylus_sdk::alloy_primitives::U256; + +// Fixed arrays map directly to Solidity fixed arrays +let fixed: [U256; 3] = [ + U256::from(1), + U256::from(2), + U256::from(3), +]; + +// Nested arrays +let nested: [[u32; 2]; 4] = [[1, 2], [3, 4], [5, 6], [7, 8]]; +``` + +## ABI encoding and decoding + +### Encoding types + +```rust +use stylus_sdk::abi::{encode, encode_params}; +use stylus_sdk::alloy_primitives::{Address, U256}; +use alloy_sol_types::{sol_data::*, SolType}; + +// Encode a single value +let value = U256::from(100); +let encoded = encode(&value); + +// Encode tuple of parameters +type TransferParams = (Address, Uint<256>); +let params = (address, amount); +let encoded = TransferParams::abi_encode_params(¶ms); +``` + +### Decoding types + +```rust +use stylus_sdk::abi::decode_params; +use stylus_sdk::alloy_primitives::{Address, U256}; +use alloy_sol_types::{sol_data::*, SolType}; + +// Define the expected type structure +type TransferParams = (Address, Uint<256>); + +// Decode from bytes +let decoded: (Address, U256) = TransferParams::abi_decode_params(&encoded_data) + .map_err(|_| "decode error")?; +``` + +### Packed encoding + +Packed encoding is useful for hashing and signature verification: + +```rust +use stylus_sdk::alloy_primitives::{Address, U256}; +use alloy_sol_types::{sol_data::*, SolType}; + +// Method 1: Using SolType::abi_encode_packed +type DataTypes = (Address, Uint<256>, String, Bytes, Uint<256>); +let data = (target, value, func, bytes, timestamp); +let packed = DataTypes::abi_encode_packed(&data); + +// Method 2: Manual concatenation +let packed_manual = [ + target.as_ref(), + &value.to_be_bytes_vec(), + func.as_bytes(), + bytes.as_ref(), + ×tamp.to_be_bytes_vec(), +].concat(); +``` + +## Error type conversions + +Stylus error types can be converted using the `Into` trait: + +```rust +use stylus_sdk::prelude::*; + +sol! { + error InvalidParam(); + error NotFound(); +} + +#[derive(SolidityError)] +pub enum MyError { + InvalidParam(InvalidParam), + NotFound(NotFound), +} + +pub fn check_value(&self, value: U256) -> Result<(), MyError> { + if value == U256::ZERO { + return Err(InvalidParam {}.into()); + } + Ok(()) +} +``` + +## Storage type conversions + +Storage types require special handling for persistence: + +```rust +use stylus_sdk::prelude::*; +use stylus_sdk::alloy_primitives::U256; + +#[storage] +pub struct Counter { + count: StorageU256, +} + +#[public] +impl Counter { + // Get value from storage + pub fn get_count(&self) -> U256 { + self.count.get() + } + + // Set value in storage + pub fn set_count(&mut self, value: U256) { + self.count.set(value); + } + + // Increment using arithmetic + pub fn increment(&mut self) { + let current = self.count.get(); + self.count.set(current + U256::from(1)); + } +} +``` + +## Best practices + +1. **Use try_from for fallible conversions**: When converting between types where overflow is possible, use `try_from` instead of panicking conversions. + +2. **Prefer native types when appropriate**: Use Rust's native `bool`, `u8`-`u128`, and `i8`-`i128` types when they match your needs exactly. They're more efficient and ergonomic. + +3. **Be explicit about byte types**: Remember that `Vec` maps to `uint8[]`, not `bytes`. Use `Bytes` from `stylus_sdk::abi` for Solidity `bytes` type. + +4. **Use the address! macro**: For hardcoded addresses, use the `address!` macro which performs compile-time validation and checksumming. + +5. **Handle conversion errors**: Always handle potential errors from `try_from`, `try_into`, and `parse` operations rather than using unwrap in production code. + +6. **Consider packed encoding for hashing**: When preparing data for hashing or signature verification, packed encoding produces more compact representations. + +## Reference + +For complete implementation details, see: + +- `[/stylus-sdk/src/abi/mod.rs](https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/stylus-sdk/src/abi/mod.rs)` - AbiType trait and encoding functions +- `[/stylus-sdk/src/abi/ints.rs](https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/stylus-sdk/src/abi/ints.rs)` - Integer type conversions +- `[/stylus-sdk/src/abi/impls.rs](https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/stylus-sdk/src/abi/impls.rs)` - Implementations for standard types +- `[/stylus-sdk/src/storage/traits.rs](https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/stylus-sdk/src/abi/traits.rs)` - Storage type conversion traits diff --git a/docs/stylus/fundamentals/data-types/primitives.mdx b/docs/stylus/fundamentals/data-types/primitives.mdx new file mode 100644 index 0000000000..0ec5f86ddc --- /dev/null +++ b/docs/stylus/fundamentals/data-types/primitives.mdx @@ -0,0 +1,564 @@ +--- +title: 'Stylus primitives' +description: 'Stylus Rust SDK primitives' +author: chrisco +sme: chrisco +sidebar_position: 1 +target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +displayed_sidebar: buildStylusSidebar +--- + +The Stylus SDK provides full support for Rust primitive types with automatic ABI encoding/decoding and Solidity type mappings. These primitives can be used in contract method signatures, storage, and as function parameters. + +## Boolean (`bool`) + +Booleans in Rust map directly to Solidity's `bool` type. + +### Usage in contract methods + +```rust +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + pub fn is_valid(&self) -> bool { + true + } + + pub fn toggle(&mut self, flag: bool) { + // Use the boolean value + if flag { + // Do something + } + } +} +``` + +### Solidity mapping + +- **Rust type**: `bool` +- **Solidity type**: `bool` +- **Storage size**: 1 byte +- **ABI signature**: `"bool"` + +## Integers + +The Stylus SDK supports both signed and unsigned integers with various bit sizes. All integer types from Rust's standard library and alloy-primitives are supported. + +### Unsigned integers + +#### Standard Rust unsigned integers + +```rust +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // u8: 8-bit unsigned integer + pub fn get_byte(&self) -> u8 { + 255 + } + + // u16: 16-bit unsigned integer + pub fn get_short(&self) -> u16 { + 65535 + } + + // u32: 32-bit unsigned integer + pub fn get_int(&self) -> u32 { + 4294967295 + } + + // u64: 64-bit unsigned integer + pub fn get_long(&self) -> u64 { + 18446744073709551615 + } + + // u128: 128-bit unsigned integer + pub fn get_u128(&self) -> u128 { + 340282366920938463463374607431768211455 + } +} +``` + +#### Alloy unsigned integers + +For larger integers and full compatibility with Solidity's uint types, use `alloy_primitives::Uint`: + +```rust +use alloy_primitives::{U256, Uint}; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // U256: 256-bit unsigned integer (most common in Solidity) + pub fn get_balance(&self) -> U256 { + U256::from(1000000) + } + + // Any bit size from 8 to 256 (in multiples of 8) + pub fn get_u160(&self) -> Uint<160, 3> { + Uint::<160, 3>::from(999) + } + + pub fn get_u96(&self) -> Uint<96, 2> { + Uint::<96, 2>::from(123456) + } +} +``` + +### Signed integers + +#### Standard Rust signed integers + +```rust +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // i8: 8-bit signed integer + pub fn get_signed_byte(&self) -> i8 { + -128 + } + + // i16: 16-bit signed integer + pub fn get_signed_short(&self) -> i16 { + -32768 + } + + // i32: 32-bit signed integer + pub fn get_signed_int(&self) -> i32 { + -2147483648 + } + + // i64: 64-bit signed integer + pub fn get_signed_long(&self) -> i64 { + -9223372036854775808 + } + + // i128: 128-bit signed integer + pub fn get_signed_i128(&self) -> i128 { + -170141183460469231731687303715884105728 + } +} +``` + +#### Alloy signed integers + +```rust +use alloy_primitives::{Signed, I256}; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // I256: 256-bit signed integer + pub fn get_signed_balance(&self) -> I256 { + I256::try_from(-1000).unwrap() + } + + // Any bit size from 8 to 256 (in multiples of 8) + pub fn get_i160(&self) -> Signed<160, 3> { + Signed::<160, 3>::try_from(-999).unwrap() + } +} +``` + +### Integer type mappings + +| Rust Type | Solidity Type | Bit Size | ABI Signature | +| ------------------------- | ------------- | -------- | ------------- | +| `u8` | `uint8` | 8 bits | `"uint8"` | +| `u16` | `uint16` | 16 bits | `"uint16"` | +| `u32` | `uint32` | 32 bits | `"uint32"` | +| `u64` | `uint64` | 64 bits | `"uint64"` | +| `u128` | `uint128` | 128 bits | `"uint128"` | +| `Uint<160, 3>` | `uint160` | 160 bits | `"uint160"` | +| `U256` / `Uint<256, 4>` | `uint256` | 256 bits | `"uint256"` | +| `i8` | `int8` | 8 bits | `"int8"` | +| `i16` | `int16` | 16 bits | `"int16"` | +| `i32` | `int32` | 32 bits | `"int32"` | +| `i64` | `int64` | 64 bits | `"int64"` | +| `i128` | `int128` | 128 bits | `"int128"` | +| `Signed<160, 3>` | `int160` | 160 bits | `"int160"` | +| `I256` / `Signed<256, 4>` | `int256` | 256 bits | `"int256"` | + +**Note**: All Solidity uint/int types from `uint8`/`int8` to `uint256`/`int256` (in 8-bit increments) are supported through `Uint` and `Signed`. + +## Address + +Ethereum addresses are represented by the `Address` type from `alloy_primitives`. + +### Basic usage + +```rust +use alloy_primitives::Address; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + pub fn get_owner(&self) -> Address { + Address::ZERO + } + + pub fn is_owner(&self, account: Address) -> bool { + account == self.vm().msg_sender() + } + + pub fn transfer_ownership(&mut self, new_owner: Address) { + // Address validation and logic + if new_owner == Address::ZERO { + // Handle error + } + } +} +``` + +### Address constants + +```rust +use alloy_primitives::Address; + +// Zero address (0x0000000000000000000000000000000000000000) +let zero = Address::ZERO; + +// Parse from string +let addr = Address::parse_checksummed("0x1234567890123456789012345678901234567890", None).unwrap(); + +// Create from bytes +let bytes: [u8; 20] = [0; 20]; +let addr = Address::from(bytes); +``` + +### Solidity mapping + +- **Rust type**: `Address` (from `alloy_primitives`) +- **Solidity type**: `address` +- **Storage size**: 20 bytes (160 bits) +- **ABI signature**: `"address"` + +## String + +Rust `String` types map to Solidity `string` type. + +### String literals + +```rust +use stylus_sdk::prelude::*; +use alloc::string::String; + +#[public] +impl MyContract { + pub fn get_name(&self) -> String { + String::from("MyToken") + } + + pub fn greet(&self, name: String) -> String { + format!("Hello, {}!", name) + } +} +``` + +### Solidity mapping + +- **Rust type**: `String` (from `alloc::string`) +- **Solidity type**: `string` +- **Storage**: Dynamic (heap-allocated) +- **ABI signature**: `"string"` +- **ABI export**: + - As argument: `"string calldata"` + - As return: `"string memory"` + +**Note**: Strings in Solidity are UTF-8 encoded byte arrays. When using strings in Stylus: + +- Use `alloc::string::String` for owned strings +- Strings are dynamically sized and stored in memory/calldata +- For storage, use `StorageString` (see [Storage Types](./storage.mdx)) + +## Bytes + +The SDK provides two types for working with byte data: + +### 1. Dynamic bytes (`Bytes`) + +For variable-length byte arrays (Solidity `bytes`): + +```rust +use alloy_primitives::Bytes; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + pub fn get_data(&self) -> Bytes { + Bytes::from(vec![1, 2, 3, 4]) + } + + pub fn process_data(&mut self, data: Bytes) -> usize { + data.len() + } +} +``` + +### 2. Fixed bytes (`FixedBytes`) + +For fixed-length byte arrays (Solidity `bytesN`): + +```rust +use alloy_primitives::FixedBytes; +use stylus_sdk::prelude::*; + +#[public] +impl MyContract { + // bytes32 (common for hashes) + pub fn get_hash(&self) -> FixedBytes<32> { + FixedBytes::<32>::ZERO + } + + // bytes2 + pub fn get_signature(&self) -> FixedBytes<2> { + FixedBytes::new([0x12, 0x34]) + } + + // Any size from 1 to 32 + pub fn get_bytes8(&self) -> FixedBytes<8> { + FixedBytes::<8>::from([1, 2, 3, 4, 5, 6, 7, 8]) + } +} +``` + +### Common FixedBytes aliases + +```rust +use alloy_primitives::{B256, B160, B128}; + +// B256 is FixedBytes<32> (bytes32) +let hash: B256 = B256::ZERO; + +// B160 is FixedBytes<20> (bytes20) +let data: B160 = B160::ZERO; + +// B128 is FixedBytes<16> (bytes16) +let value: B128 = B128::ZERO; +``` + +### Bytes type mappings + +| Rust Type | Solidity Type | Description | +| ------------------------- | ------------- | -------------------------------- | +| `Bytes` | `bytes` | Dynamic byte array | +| `Vec` | `uint8[]` | Array of bytes (NOT `bytes`!) | +| `FixedBytes` | `bytesN` | Fixed-size byte array (N = 1-32) | +| `B256` / `FixedBytes<32>` | `bytes32` | 32-byte array (hashes) | +| `B160` / `FixedBytes<20>` | `bytes20` | 20-byte array | +| `B128` / `FixedBytes<16>` | `bytes16` | 16-byte array | + +**Important Distinction**: + +- `Vec` maps to Solidity `uint8[]` (array of unsigned integers) +- `Bytes` maps to Solidity `bytes` (dynamic byte array) +- For Solidity `bytes`, always use `alloy_primitives::Bytes` + +### Bytes ABI encoding + +```rust +// Bytes type +// ABI signature: "bytes" +// As argument: "bytes calldata" +// As return: "bytes memory" + +// FixedBytes type +// ABI signature: "bytesN" where N is 1-32 +// Example: FixedBytes<32> -> "bytes32" +``` + +## Hex string literals + +When working with hex data, you can use hex literals: + +```rust +use alloy_primitives::{hex, Address, FixedBytes, Bytes}; + +// Hex bytes +let data = hex!("deadbeef"); + +// Address from hex +let addr = Address::from(hex!("1234567890123456789012345678901234567890")); + +// FixedBytes from hex +let hash = FixedBytes::<32>::from(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" +)); + +// Dynamic Bytes from hex +let bytes = Bytes::from(hex!("aabbccdd")); +``` + +## Complete example + +Here's a comprehensive example showing all primitive types: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloc::string::String; +use alloy_primitives::{Address, Bytes, FixedBytes, U256}; +use stylus_sdk::prelude::*; + +sol_storage! { + #[entrypoint] + pub struct PrimitiveExample { + bool initialized; + uint256 count; + address owner; + } +} + +#[public] +impl PrimitiveExample { + // Boolean + pub fn is_initialized(&self) -> bool { + self.initialized.get() + } + + // Unsigned integers (native Rust) + pub fn get_u8(&self) -> u8 { + 255 + } + + pub fn get_u256(&self) -> U256 { + self.count.get() + } + + // Signed integers + pub fn get_signed(&self) -> i32 { + -42 + } + + // Address + pub fn get_owner(&self) -> Address { + self.owner.get() + } + + pub fn set_owner(&mut self, new_owner: Address) { + self.owner.set(new_owner); + } + + // String + pub fn get_name(&self) -> String { + String::from("PrimitiveExample") + } + + // Dynamic bytes + pub fn get_data(&self) -> Bytes { + Bytes::from(vec![1, 2, 3, 4]) + } + + // Fixed bytes + pub fn get_hash(&self) -> FixedBytes<32> { + FixedBytes::<32>::ZERO + } + + // Multiple parameters + pub fn complex_function( + &mut self, + flag: bool, + amount: U256, + recipient: Address, + data: Bytes + ) -> bool { + // Function logic + true + } +} +``` + +## Best practices + +1. **Use U256 for token amounts**: Solidity commonly uses `uint256` for token balances and amounts. + + ```rust + use alloy_primitives::U256; + + pub fn transfer(&mut self, amount: U256) { + // amount is uint256 in Solidity + } + ``` + +2. **Use Address for account addresses**: Always use `alloy_primitives::Address` for Ethereum addresses. + + ```rust + use alloy_primitives::Address; + + pub fn get_balance(&self, account: Address) -> U256 { + // Query balance + } + ``` + +3. **Use Bytes for dynamic byte data**: For Solidity `bytes`, use `alloy_primitives::Bytes`, not `Vec`. + + ```rust + use alloy_primitives::Bytes; + + pub fn process(&self, data: Bytes) { + // data maps to Solidity bytes + } + ``` + +4. **Use FixedBytes for hashes and signatures**: For fixed-size byte data like hashes. + + ```rust + use alloy_primitives::FixedBytes; + + pub fn verify(&self, hash: FixedBytes<32>) -> bool { + // hash maps to Solidity bytes32 + true + } + ``` + +5. **Check for zero addresses**: Always validate addresses before use. + + ```rust + use alloy_primitives::Address; + + pub fn set_admin(&mut self, admin: Address) { + if admin == Address::ZERO { + // Handle error + } + } + ``` + +## Type conversion + +### Between integer types + +```rust +use alloy_primitives::U256; + +// Native to U256 +let amount: u64 = 1000; +let big_amount = U256::from(amount); + +// U256 to native (with bounds checking) +let big_value = U256::from(1000); +let small_value: u64 = big_value.to::(); +``` + +### Address conversions + +```rust +use alloy_primitives::Address; + +// From bytes +let bytes: [u8; 20] = [0; 20]; +let addr = Address::from(bytes); + +// To bytes +let addr = Address::ZERO; +let bytes: [u8; 20] = addr.into(); +``` + +## See also + +- [Compound Types](./compound-types.mdx) - Arrays, tuples, structs +- [Storage Types](./storage.mdx) - Persistent storage for primitives +- [Type Conversions](./conversions-between-types.mdx) - Converting between types diff --git a/docs/stylus/fundamentals/data-types/storage.mdx b/docs/stylus/fundamentals/data-types/storage.mdx new file mode 100644 index 0000000000..d2ab17f1f6 --- /dev/null +++ b/docs/stylus/fundamentals/data-types/storage.mdx @@ -0,0 +1,1107 @@ +--- +title: 'Stylus Rust SDK storage' +description: 'Stylus Rust SDK storage types' +author: anegg0 +sme: anegg0 +sidebar_position: 3 +target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +displayed_sidebar: buildStylusSidebar +--- + +Persistent storage in Stylus contracts provides access to the EVM State Trie, the same key-value storage used by Solidity contracts. The SDK provides type-safe storage access through dedicated storage types that prevent aliasing errors at compile time using Rust's borrow checker. + +## Overview + +Stylus contracts share the same persistent storage as Solidity contracts: + +- Both Stylus and Solidity access the same EVM State Trie +- Storage is fully interoperable between Stylus and Solidity contracts +- Stylus provides compile-time safety through Rust's type system +- Storage operations are cached for gas efficiency + +The Stylus SDK provides a comprehensive hierarchy of storage types: + +![Storage Types Hierarchy](/img/stylus-storage-types-hierarchy.png) + +_Figure 3: Storage type hierarchy showing primitives, collections, and custom struct options._ + +### Storage declaration + +Use the `sol_storage!` macro to define contract storage with Solidity-compatible layout: + +```rust +use stylus_sdk::prelude::*; + +sol_storage! { + #[entrypoint] + pub struct MyContract { + uint256 count; + address owner; + bool initialized; + } +} +``` + +Alternatively, use the `#[storage]` attribute for Rust-style declarations: + +```rust +use stylus_sdk::prelude::*; +use stylus_sdk::storage::*; + +#[storage] +#[entrypoint] +pub struct MyContract { + count: StorageU256, + owner: StorageAddress, + initialized: StorageBool, +} +``` + +## Storage primitives + +Storage primitives are persistent versions of basic types. + +### Boolean storage (`StorageBool`) + +Store boolean values in persistent storage: + +```rust +use stylus_sdk::prelude::*; + +sol_storage! { + #[entrypoint] + pub struct Contract { + bool is_initialized; + bool is_paused; + } +} + +#[public] +impl Contract { + pub fn initialize(&mut self) { + self.is_initialized.set(true); + } + + pub fn is_initialized(&self) -> bool { + self.is_initialized.get() + } + + pub fn toggle_pause(&mut self) { + let current = self.is_paused.get(); + self.is_paused.set(!current); + } +} +``` + +### Integer storage + +Store unsigned and signed integers with various bit sizes: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::U256; + +sol_storage! { + #[entrypoint] + pub struct Counter { + uint256 count; + uint64 timestamp; + int256 balance; + } +} + +#[public] +impl Counter { + // Unsigned integer operations + pub fn increment(&mut self) { + let current = self.count.get(); + self.count.set(current + U256::from(1)); + } + + pub fn add(&mut self, value: U256) { + let current = self.count.get(); + self.count.set(current + value); + } + + pub fn get_count(&self) -> U256 { + self.count.get() + } + + // Timestamp storage + pub fn set_timestamp(&mut self, ts: u64) { + self.timestamp.set(ts); + } +} +``` + +#### Available storage integer types + +| Storage Type | Primitive Type | Bit Size | Solidity Type | +| ------------- | -------------- | -------- | ------------- | +| `StorageU8` | `U8` | 8 bits | `uint8` | +| `StorageU16` | `U16` | 16 bits | `uint16` | +| `StorageU32` | `U32` | 32 bits | `uint32` | +| `StorageU64` | `U64` | 64 bits | `uint64` | +| `StorageU128` | `U128` | 128 bits | `uint128` | +| `StorageU256` | `U256` | 256 bits | `uint256` | +| `StorageI8` | `I8` | 8 bits | `int8` | +| `StorageI16` | `I16` | 16 bits | `int16` | +| `StorageI32` | `I32` | 32 bits | `int32` | +| `StorageI64` | `I64` | 64 bits | `int64` | +| `StorageI128` | `I128` | 128 bits | `int128` | +| `StorageI256` | `I256` | 256 bits | `int256` | + +#### Integer update operations + +`StorageUint` types provide convenient update methods: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::U256; + +sol_storage! { + #[entrypoint] + pub struct Contract { + uint256 balance; + } +} + +#[public] +impl Contract { + // Wrapping operations (overflow wraps around) + pub fn add_wrapping(&mut self, value: U256) -> U256 { + self.balance.update_wrap_add(value) + } + + pub fn sub_wrapping(&mut self, value: U256) -> U256 { + self.balance.update_wrap_sub(value) + } + + pub fn mul_wrapping(&mut self, value: U256) -> U256 { + self.balance.update_wrap_mul(value) + } + + // Checked operations (return None on overflow) + pub fn add_checked(&mut self, value: U256) -> Option { + self.balance.update_check_add(value) + } + + pub fn sub_checked(&mut self, value: U256) -> Option { + self.balance.update_check_sub(value) + } +} +``` + +### Address storage (`StorageAddress`) + +Store Ethereum addresses: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::Address; + +sol_storage! { + #[entrypoint] + pub struct Ownership { + address owner; + address pending_owner; + } +} + +#[public] +impl Ownership { + pub fn get_owner(&self) -> Address { + self.owner.get() + } + + pub fn transfer_ownership(&mut self, new_owner: Address) { + // Validate address + if new_owner == Address::ZERO { + // Handle error + return; + } + + let current_owner = self.owner.get(); + if self.vm().msg_sender() != current_owner { + // Not authorized + return; + } + + self.pending_owner.set(new_owner); + } + + pub fn accept_ownership(&mut self) { + let caller = self.vm().msg_sender(); + if caller != self.pending_owner.get() { + return; + } + + self.owner.set(caller); + self.pending_owner.set(Address::ZERO); + } +} +``` + +### Fixed bytes storage + +Store fixed-size byte arrays: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::FixedBytes; + +sol_storage! { + #[entrypoint] + pub struct Hashes { + bytes32 merkle_root; + bytes32 commitment; + bytes4 selector; + } +} + +#[public] +impl Hashes { + pub fn set_merkle_root(&mut self, root: FixedBytes<32>) { + self.merkle_root.set(root); + } + + pub fn get_merkle_root(&self) -> FixedBytes<32> { + self.merkle_root.get() + } + + pub fn verify_hash(&self, proof: FixedBytes<32>) -> bool { + self.merkle_root.get() == proof + } +} +``` + +#### Available fixed bytes storage types + +| Storage Type | Bytes | Bits | Solidity Type | +| ------------- | ----- | -------- | ------------- | +| `StorageB8` | 1 | 8 bits | `bytes1` | +| `StorageB16` | 2 | 16 bits | `bytes2` | +| `StorageB32` | 4 | 32 bits | `bytes4` | +| `StorageB64` | 8 | 64 bits | `bytes8` | +| `StorageB128` | 16 | 128 bits | `bytes16` | +| `StorageB160` | 20 | 160 bits | `bytes20` | +| `StorageB224` | 28 | 224 bits | `bytes28` | +| `StorageB256` | 32 | 256 bits | `bytes32` | + +## Storage collections + +Storage collections provide persistent arrays, vectors, and maps. + +### StorageVec (dynamic array) + +Dynamic arrays that can grow and shrink: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +sol_storage! { + #[entrypoint] + pub struct TokenList { + address[] holders; + uint256[] balances; + } +} + +#[public] +impl TokenList { + // Add element + pub fn add_holder(&mut self, holder: Address) { + self.holders.push(holder); + } + + // Get element + pub fn get_holder(&self, index: U256) -> Address { + self.holders.get(index).unwrap() + } + + // Get length + pub fn holder_count(&self) -> U256 { + U256::from(self.holders.len()) + } + + // Set element + pub fn set_balance(&mut self, index: U256, balance: U256) { + self.balances.setter(index).unwrap().set(balance); + } + + // Iterate over elements + pub fn total_balance(&self) -> U256 { + let mut total = U256::ZERO; + for i in 0..self.balances.len() { + total += self.balances.get(U256::from(i)).unwrap(); + } + total + } + + // Remove element (erase to zero) + pub fn remove_holder(&mut self, index: U256) { + self.holders.setter(index).unwrap().erase(); + } + + // Clear all elements + pub fn clear_holders(&mut self) { + self.holders.erase(); + } +} +``` + +#### StorageVec methods + +```rust +// Length operations +fn len(&self) -> usize +fn is_empty(&self) -> bool + +// Access operations +fn get(&self, index: impl TryInto) -> Option +fn getter(&self, index: impl TryInto) -> Option> +fn setter(&mut self, index: impl TryInto) -> Option> + +// Mutation operations +fn push(&mut self, value: T) +fn grow(&mut self) -> StorageGuardMut<'_, T> // Add new element and return mutable reference +fn erase(&mut self) // Clear all elements +``` + +### StorageArray (fixed array) + +Fixed-size arrays with compile-time known length: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::U256; + +sol_storage! { + #[entrypoint] + pub struct FixedData { + uint256[10] values; + address[5] admins; + } +} + +#[public] +impl FixedData { + // Get element + pub fn get_value(&self, index: U256) -> U256 { + self.values.get(index).unwrap() + } + + // Set element + pub fn set_value(&mut self, index: U256, value: U256) { + self.values.setter(index).unwrap().set(value); + } + + // Get array length (compile-time constant) + pub fn array_size(&self) -> U256 { + U256::from(self.values.len()) + } + + // Iterate over array + pub fn sum_values(&self) -> U256 { + let mut sum = U256::ZERO; + for i in 0..self.values.len() { + sum += self.values.get(U256::from(i)).unwrap(); + } + sum + } +} +``` + +### StorageMap (mapping) + +Key-value storage, equivalent to Solidity `mapping`: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +sol_storage! { + #[entrypoint] + pub struct Token { + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) allowances; + } +} + +#[public] +impl Token { + // Get value (returns zero if not set) + pub fn balance_of(&self, account: Address) -> U256 { + self.balances.get(account) + } + + // Set value + pub fn set_balance(&mut self, account: Address, amount: U256) { + self.balances.setter(account).set(amount); + } + + // Insert value (same as set) + pub fn mint(&mut self, account: Address, amount: U256) { + let current = self.balances.get(account); + self.balances.insert(account, current + amount); + } + + // Delete value (reset to zero) + pub fn burn(&mut self, account: Address, amount: U256) { + let current = self.balances.get(account); + if current >= amount { + self.balances.setter(account).set(current - amount); + } + } + + // Nested mapping + pub fn allowance(&self, owner: Address, spender: Address) -> U256 { + self.allowances.get(owner).get(spender) + } + + pub fn approve(&mut self, spender: Address, amount: U256) { + let owner = self.vm().msg_sender(); + self.allowances + .setter(owner) + .setter(spender) + .set(amount); + } +} +``` + +#### StorageMap methods + +```rust +// Read operations +fn get(&self, key: K) -> V // Returns zero-value if not present +fn getter(&self, key: K) -> StorageGuard<'_, V> + +// Write operations +fn setter(&mut self, key: K) -> StorageGuardMut<'_, V> +fn insert(&mut self, key: K, value: V) +fn replace(&mut self, key: K, value: V) -> V // Returns old value +fn take(&mut self, key: K) -> V // Returns value and deletes +fn delete(&mut self, key: K) // Erases entry +``` + +#### Supported map key types + +Any type implementing `StorageKey` can be used as a map key: + +- `Address` +- `U256`, `U160`, and other `Uint` types +- `FixedBytes` +- `Signed` types +- `bool` + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256, FixedBytes}; + +sol_storage! { + #[entrypoint] + pub struct MultiMap { + mapping(address => uint256) by_address; + mapping(uint256 => address) by_id; + mapping(bytes32 => bool) by_hash; + mapping(bool => uint256) by_flag; + } +} +``` + +### StorageString and StorageBytes + +Dynamic string and bytes storage: + +```rust +use stylus_sdk::prelude::*; +use alloc::string::String; + +sol_storage! { + #[entrypoint] + pub struct Metadata { + string name; + string symbol; + bytes data; + } +} + +#[public] +impl Metadata { + // String operations + pub fn get_name(&self) -> String { + self.name.get_string() + } + + pub fn set_name(&mut self, name: String) { + self.name.set_str(name); + } + + pub fn name_length(&self) -> usize { + self.name.len() + } + + pub fn clear_name(&mut self) { + self.name.erase(); + } + + // Bytes operations + pub fn get_data(&self) -> Vec { + self.data.get_bytes() + } + + pub fn set_data(&mut self, data: Vec) { + self.data.set_bytes(data); + } + + pub fn data_length(&self) -> usize { + self.data.len() + } +} +``` + +## Storage structs + +Define custom storage types with nested structures: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +// Storage struct definition +#[storage] +pub struct UserInfo { + balance: StorageU256, + is_active: StorageBool, + timestamp: StorageU64, +} + +sol_storage! { + #[entrypoint] + pub struct UserRegistry { + mapping(address => UserInfo) users; + uint256 total_users; + } +} + +#[public] +impl UserRegistry { + pub fn register_user(&mut self, user: Address) { + let mut user_info = self.users.setter(user); + user_info.balance.set(U256::ZERO); + user_info.is_active.set(true); + user_info.timestamp.set(self.vm().block_timestamp()); + + let count = self.total_users.get(); + self.total_users.set(count + U256::from(1)); + } + + pub fn get_balance(&self, user: Address) -> U256 { + self.users.get(user).balance.get() + } + + pub fn update_balance(&mut self, user: Address, amount: U256) { + self.users.setter(user).balance.set(amount); + } + + pub fn is_active(&self, user: Address) -> bool { + self.users.get(user).is_active.get() + } +} +``` + +### Nested storage structs + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::Address; + +#[storage] +pub struct Dog { + name: StorageString, + breed: StorageString, +} + +#[storage] +pub struct User { + name: StorageString, + dogs: StorageVec, +} + +sol_storage! { + #[entrypoint] + pub struct Registry { + mapping(address => User) users; + } +} + +#[public] +impl Registry { + pub fn add_dog(&mut self, owner: Address, name: String, breed: String) { + let mut user = self.users.setter(owner); + let mut dog = user.dogs.grow(); + dog.name.set_str(name); + dog.breed.set_str(breed); + } + + pub fn get_dog_count(&self, owner: Address) -> usize { + self.users.get(owner).dogs.len() + } + + pub fn get_dog_name(&self, owner: Address, index: usize) -> String { + self.users + .get(owner) + .dogs + .get(index) + .unwrap() + .name + .get_string() + } +} +``` + +## Storage patterns + +### Initialization pattern + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +sol_storage! { + #[entrypoint] + pub struct Contract { + bool initialized; + address owner; + uint256 value; + } +} + +#[public] +impl Contract { + #[constructor] + pub fn constructor(&mut self, initial_value: U256) { + self.owner.set(self.vm().msg_sender()); + self.value.set(initial_value); + self.initialized.set(true); + } + + fn only_initialized(&self) { + if !self.initialized.get() { + // Revert: not initialized + } + } + + pub fn get_value(&self) -> U256 { + self.only_initialized(); + self.value.get() + } +} +``` + +### Counter pattern + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::U256; + +sol_storage! { + #[entrypoint] + pub struct Counter { + uint256 count; + mapping(address => uint256) user_counts; + } +} + +#[public] +impl Counter { + pub fn increment(&mut self) { + let current = self.count.get(); + self.count.set(current + U256::from(1)); + } + + pub fn increment_by(&mut self, amount: U256) { + let current = self.count.get(); + self.count.set(current + amount); + } + + pub fn increment_user(&mut self) { + let user = self.vm().msg_sender(); + let current = self.user_counts.get(user); + self.user_counts.insert(user, current + U256::from(1)); + } + + pub fn get_count(&self) -> U256 { + self.count.get() + } + + pub fn get_user_count(&self, user: Address) -> U256 { + self.user_counts.get(user) + } +} +``` + +### Access control pattern + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::Address; + +sol_storage! { + #[entrypoint] + pub struct AccessControl { + address owner; + mapping(address => bool) admins; + mapping(address => bool) users; + } +} + +#[public] +impl AccessControl { + #[constructor] + pub fn constructor(&mut self) { + let sender = self.vm().msg_sender(); + self.owner.set(sender); + self.admins.insert(sender, true); + } + + fn only_owner(&self) { + if self.vm().msg_sender() != self.owner.get() { + // Revert: not owner + } + } + + fn only_admin(&self) { + let sender = self.vm().msg_sender(); + if !self.admins.get(sender) { + // Revert: not admin + } + } + + pub fn add_admin(&mut self, admin: Address) { + self.only_owner(); + self.admins.insert(admin, true); + } + + pub fn remove_admin(&mut self, admin: Address) { + self.only_owner(); + self.admins.delete(admin); + } + + pub fn add_user(&mut self, user: Address) { + self.only_admin(); + self.users.insert(user, true); + } + + pub fn is_admin(&self, account: Address) -> bool { + self.admins.get(account) + } + + pub fn is_user(&self, account: Address) -> bool { + self.users.get(account) + } +} +``` + +### Registry pattern + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +#[storage] +pub struct Record { + owner: StorageAddress, + created_at: StorageU64, + updated_at: StorageU64, + active: StorageBool, +} + +sol_storage! { + #[entrypoint] + pub struct Registry { + mapping(bytes32 => Record) records; + mapping(address => bytes32[]) user_records; + uint256 total_records; + } +} + +#[public] +impl Registry { + pub fn create_record(&mut self, id: FixedBytes<32>) { + let now = self.vm().block_timestamp(); + let owner = self.vm().msg_sender(); + + let mut record = self.records.setter(id); + record.owner.set(owner); + record.created_at.set(now); + record.updated_at.set(now); + record.active.set(true); + + // Add to user's record list + self.user_records.setter(owner).push(id); + + // Increment total + let total = self.total_records.get(); + self.total_records.set(total + U256::from(1)); + } + + pub fn get_record_owner(&self, id: FixedBytes<32>) -> Address { + self.records.get(id).owner.get() + } + + pub fn is_active(&self, id: FixedBytes<32>) -> bool { + self.records.get(id).active.get() + } + + pub fn deactivate(&mut self, id: FixedBytes<32>) { + let owner = self.records.get(id).owner.get(); + if owner != self.vm().msg_sender() { + // Not authorized + return; + } + + self.records.setter(id).active.set(false); + } +} +``` + +## Best practices + +### 1. Use appropriate storage types + +```rust +// Good: Use StorageU256 for counters +sol_storage! { + pub struct Counter { + uint256 count; + } +} + +// Good: Use StorageMap for lookups +sol_storage! { + pub struct Balances { + mapping(address => uint256) balances; + } +} + +// Good: Use StorageVec for dynamic lists +sol_storage! { + pub struct Users { + address[] user_list; + } +} +``` + +### 2. Minimize storage operations + +```rust +// Bad: Multiple storage reads +pub fn bad_example(&self) -> U256 { + let a = self.value.get(); + let b = self.value.get(); // Unnecessary read + a + b +} + +// Good: Single storage read +pub fn good_example(&self) -> U256 { + let value = self.value.get(); + value + value +} +``` + +### 3. Use batch operations + +```rust +// Good: Batch updates in a single transaction +pub fn update_multiple(&mut self, values: Vec) { + for (i, value) in values.iter().enumerate() { + self.data.setter(U256::from(i)).unwrap().set(*value); + } +} +``` + +### 4. Check before delete + +```rust +// Good: Verify before deletion +pub fn remove_user(&mut self, user: Address) { + if self.users.get(user) { + self.users.delete(user); + // Update related storage + } +} +``` + +### 5. Use erase for gas refunds + +```rust +// Good: Clear storage for gas refunds +pub fn clear_data(&mut self) { + self.data.erase(); // Refunds gas +} +``` + +## Storage slots and layout + +Stylus uses the same storage layout as Solidity: + +- Each storage slot is **32 bytes** (256 bits) +- Variables are **packed** when possible to save space +- Arrays and mappings use **computed slots** via hashing + +![Storage Slots Layout](/img/stylus-storage-slots.png) + +_Figure: Storage slot layout showing how variables are packed into 32-byte slots and how mapping values are computed using keccak256 hashing._ + +```rust +sol_storage! { + pub struct Packed { + uint128 a; // Slot 0 (first 16 bytes) + uint128 b; // Slot 0 (last 16 bytes) + uint256 c; // Slot 1 (full 32 bytes) + bool d; // Slot 2 (1 byte) + address e; // Slot 2 (20 bytes, packed with d) + } +} +``` + +### Custom storage slots + +You can specify custom storage slots for specific use cases: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::U256; + +#[storage] +#[entrypoint] +pub struct CustomSlots { + // Default slot allocation + value: StorageU256, + + // Custom slot (advanced usage) + // Note: Requires manual slot management +} +``` + +## Complete example + +Here's a comprehensive example demonstrating various storage types: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloc::string::String; +use alloy_primitives::{Address, FixedBytes, U256}; +use stylus_sdk::prelude::*; + +#[storage] +pub struct TokenMetadata { + name: StorageString, + symbol: StorageString, + decimals: StorageU8, +} + +sol_storage! { + #[entrypoint] + pub struct Token { + // Primitives + uint256 total_supply; + bool paused; + address owner; + + // Collections + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) allowances; + address[] holders; + + // Nested struct + TokenMetadata metadata; + } +} + +#[public] +impl Token { + #[constructor] + pub fn constructor(&mut self, name: String, symbol: String) { + self.owner.set(self.vm().msg_sender()); + self.metadata.name.set_str(name); + self.metadata.symbol.set_str(symbol); + self.metadata.decimals.set(18); + self.paused.set(false); + } + + pub fn total_supply(&self) -> U256 { + self.total_supply.get() + } + + pub fn balance_of(&self, account: Address) -> U256 { + self.balances.get(account) + } + + pub fn transfer(&mut self, to: Address, amount: U256) -> bool { + if self.paused.get() { + return false; + } + + let from = self.vm().msg_sender(); + let from_balance = self.balances.get(from); + + if from_balance < amount { + return false; + } + + self.balances.insert(from, from_balance - amount); + + let to_balance = self.balances.get(to); + self.balances.insert(to, to_balance + amount); + + true + } + + pub fn approve(&mut self, spender: Address, amount: U256) -> bool { + let owner = self.vm().msg_sender(); + self.allowances.setter(owner).insert(spender, amount); + true + } + + pub fn allowance(&self, owner: Address, spender: Address) -> U256 { + self.allowances.get(owner).get(spender) + } + + pub fn pause(&mut self) { + if self.vm().msg_sender() != self.owner.get() { + return; + } + self.paused.set(true); + } + + pub fn unpause(&mut self) { + if self.vm().msg_sender() != self.owner.get() { + return; + } + self.paused.set(false); + } + + pub fn name(&self) -> String { + self.metadata.name.get_string() + } + + pub fn symbol(&self) -> String { + self.metadata.symbol.get_string() + } + + pub fn decimals(&self) -> u8 { + self.metadata.decimals.get() + } +} +``` + +## See also + +- [Primitives](./primitives.mdx) - Basic types used in storage +- [Compound Types](./compound-types.mdx) - Complex types in storage +- [Type Conversions](./conversions-between-types.mdx) - Converting between types diff --git a/docs/stylus/fundamentals/global-variables-and-functions.mdx b/docs/stylus/fundamentals/global-variables-and-functions.mdx new file mode 100644 index 0000000000..8d0bafbb19 --- /dev/null +++ b/docs/stylus/fundamentals/global-variables-and-functions.mdx @@ -0,0 +1,894 @@ +--- +title: 'Global variables and functions' +description: 'Stylus Rust SDK global variables and functions for blockchain context access' +author: chrisco +sme: chrisco +sidebar_position: 3 +target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +displayed_sidebar: buildStylusSidebar +--- + +Stylus contracts access blockchain context and utilities through the VM (Virtual Machine) interface via `self.vm()`. This provides access to message context, block information, transaction details, account data, cryptographic functions, and gas metering. + +## Accessing the VM + +All public contract methods have access to the VM context through `self.vm()`: + +```rust +use stylus_sdk::prelude::*; +use alloy_primitives::{Address, U256}; + +#[public] +impl MyContract { + pub fn get_context_info(&self) -> (Address, U256, u64) { + let vm = self.vm(); + ( + vm.msg_sender(), // Caller's address + vm.msg_value(), // ETH sent with call + vm.block_number(), // Current block number + ) + } +} +``` + +The VM provides methods organized into several categories: + +## Message Context (`msg`) + +Methods for accessing information about the current call. + +### `msg_sender()` + +Gets the address of the account that called the program. + +```rust +pub fn msg_sender(&self) -> Address +``` + +**Equivalent to**: Solidity's `msg.sender` + +**Example:** + +```rust +#[public] +impl Token { + pub fn transfer(&mut self, to: Address, amount: U256) -> bool { + let from = self.vm().msg_sender(); + // Transfer from the caller to recipient + self._transfer(from, to, amount) + } +} +``` + +**Important Notes:** + +- For normal L2-to-L2 transactions, behaves like EVM's `CALLER` opcode +- For L1-to-L2 retryable ticket transactions, the top-level sender's address will be [aliased](https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing) +- In delegate calls, returns the original caller (not the delegating contract) + +### `msg_value()` + +Gets the ETH value in wei sent to the program. + +```rust +pub fn msg_value(&self) -> U256 +``` + +**Equivalent to**: Solidity's `msg.value` + +**Example:** + +```rust +#[public] +impl PaymentContract { + #[payable] + pub fn deposit(&mut self) -> U256 { + let amount = self.vm().msg_value(); + let sender = self.vm().msg_sender(); + + let balance = self.balances.get(sender); + self.balances.setter(sender).set(balance + amount); + + amount + } +} +``` + +**Note:** Only functions marked with `#[payable]` can receive ETH. Non-payable functions will revert if `msg_value() > 0`. + +### `msg_reentrant()` + +Checks whether the current call is reentrant. + +```rust +pub fn msg_reentrant(&self) -> bool +``` + +**Example:** + +```rust +#[public] +impl Vault { + pub fn withdraw(&mut self, amount: U256) { + if self.vm().msg_reentrant() { + // Handle reentrancy + panic!("Reentrant call detected"); + } + // Withdrawal logic... + } +} +``` + +**Note:** By default, Stylus contracts prevent reentrancy unless the `reentrant` feature is enabled. + +## Transaction Context (`tx`) + +Methods for accessing information about the current transaction. + +### `tx_origin()` + +Gets the top-level sender of the transaction. + +```rust +pub fn tx_origin(&self) -> Address +``` + +**Equivalent to**: Solidity's `tx.origin` + +**Example:** + +```rust +#[public] +impl Factory { + #[constructor] + pub fn constructor(&mut self) { + // Use tx_origin when deploying via a factory + let deployer = self.vm().tx_origin(); + self.owner.set(deployer); + } +} +``` + +**Important:** Returns the original EOA (Externally Owned Account) that initiated the transaction, even through multiple contract calls. + +### `tx_gas_price()` + +Gets the gas price in wei per gas, which on Arbitrum chains equals the basefee. + +```rust +pub fn tx_gas_price(&self) -> U256 +``` + +**Equivalent to**: Solidity's `tx.gasprice` + +**Example:** + +```rust +#[public] +impl Analytics { + pub fn record_gas_price(&mut self) { + let price = self.vm().tx_gas_price(); + self.gas_prices.push(price); + } +} +``` + +### `tx_ink_price()` + +Gets the price of ink in EVM gas basis points. + +```rust +pub fn tx_ink_price(&self) -> u32 +``` + +**Description:** Stylus uses "ink" as its unit of computation. This method returns the conversion rate from ink to gas. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/gas-metering) for more information. + +**Example:** + +```rust +#[public] +impl Contract { + pub fn get_ink_price(&self) -> u32 { + self.vm().tx_ink_price() + } +} +``` + +## Block Context (`block`) + +Methods for accessing information about the current block. + +### `block_number()` + +Gets a bounded estimate of the L1 block number at which the Sequencer sequenced the transaction. + +```rust +pub fn block_number(&self) -> u64 +``` + +**Equivalent to**: Solidity's `block.number` + +**Example:** + +```rust +#[public] +impl TimeLock { + pub fn lock_until(&mut self, blocks: u64) { + let unlock_block = self.vm().block_number() + blocks; + self.unlock_block.set(U256::from(unlock_block)); + } + + pub fn can_unlock(&self) -> bool { + let current = self.vm().block_number(); + let unlock = self.unlock_block.get().try_into().unwrap_or(u64::MAX); + current >= unlock + } +} +``` + +**Note:** See [Block Numbers and Time](https://developer.arbitrum.io/time) for more information on how this value is determined on Arbitrum. + +### `block_timestamp()` + +Gets a bounded estimate of the Unix timestamp at which the Sequencer sequenced the transaction. + +```rust +pub fn block_timestamp(&self) -> u64 +``` + +**Equivalent to**: Solidity's `block.timestamp` + +**Example:** + +```rust +#[public] +impl Auction { + pub fn place_bid(&mut self, amount: U256) { + let now = self.vm().block_timestamp(); + let deadline = self.deadline.get().try_into().unwrap_or(0); + + if now > deadline { + panic!("Auction ended"); + } + + // Process bid... + } +} +``` + +**Note:** See [Block Numbers and Time](https://developer.arbitrum.io/time) for more information on how this value is determined on Arbitrum. + +### `block_basefee()` + +Gets the basefee of the current block. + +```rust +pub fn block_basefee(&self) -> U256 +``` + +**Equivalent to**: Solidity's `block.basefee` + +**Example:** + +```rust +#[public] +impl FeeTracker { + pub fn current_basefee(&self) -> U256 { + self.vm().block_basefee() + } +} +``` + +### `block_coinbase()` + +Gets the coinbase of the current block. + +```rust +pub fn block_coinbase(&self) -> Address +``` + +**Equivalent to**: Solidity's `block.coinbase` + +**Important:** On Arbitrum chains, this is the L1 batch poster's address, which differs from Ethereum where the validator determines the coinbase. + +**Example:** + +```rust +#[public] +impl Contract { + pub fn get_batch_poster(&self) -> Address { + self.vm().block_coinbase() + } +} +``` + +### `block_gas_limit()` + +Gets the gas limit of the current block. + +```rust +pub fn block_gas_limit(&self) -> u64 +``` + +**Equivalent to**: Solidity's `block.gaslimit` + +**Example:** + +```rust +#[public] +impl Contract { + pub fn check_gas_limit(&self) -> bool { + let limit = self.vm().block_gas_limit(); + limit > 30_000_000 + } +} +``` + +## Chain Context + +Methods for accessing chain-specific information. + +### `chain_id()` + +Gets the unique chain identifier of the Arbitrum chain. + +```rust +pub fn chain_id(&self) -> u64 +``` + +**Equivalent to**: Solidity's `block.chainid` + +**Example:** + +```rust +#[public] +impl MultiChain { + pub fn verify_chain(&self, expected_chain: u64) -> bool { + self.vm().chain_id() == expected_chain + } +} +``` + +**Common Arbitrum Chain IDs:** + +- Arbitrum One: 42161 +- Arbitrum Nova: 42170 +- Arbitrum Sepolia (testnet): 421614 + +## Account Information + +Methods for querying account details. + +### `contract_address()` + +Gets the address of the current program. + +```rust +pub fn contract_address(&self) -> Address +``` + +**Equivalent to**: Solidity's `address(this)` + +**Example:** + +```rust +#[public] +impl Contract { + pub fn this_address(&self) -> Address { + self.vm().contract_address() + } + + pub fn this_balance(&self) -> U256 { + let addr = self.vm().contract_address(); + self.vm().balance(addr) + } +} +``` + +### `balance(address)` + +Gets the ETH balance in wei of the account at the given address. + +```rust +pub fn balance(&self, account: Address) -> U256 +``` + +**Equivalent to**: Solidity's `address.balance` + +**Example:** + +```rust +#[public] +impl BalanceChecker { + pub fn get_balance(&self, account: Address) -> U256 { + self.vm().balance(account) + } + + pub fn has_sufficient_balance(&self, account: Address, required: U256) -> bool { + self.vm().balance(account) >= required + } +} +``` + +### `code(address)` + +Gets the code from the account at the given address. + +```rust +pub fn code(&self, account: Address) -> Vec +``` + +**Equivalent to**: Solidity's `address.code` (similar to `EXTCODECOPY` opcode) + +**Example:** + +```rust +#[public] +impl Contract { + pub fn is_contract(&self, account: Address) -> bool { + self.vm().code(account).len() > 0 + } +} +``` + +### `code_size(address)` + +Gets the size of the code in bytes at the given address. + +```rust +pub fn code_size(&self, account: Address) -> usize +``` + +**Equivalent to**: Solidity's `EXTCODESIZE` opcode + +**Example:** + +```rust +#[public] +impl Contract { + pub fn get_code_size(&self, account: Address) -> usize { + self.vm().code_size(account) + } +} +``` + +### `code_hash(address)` + +Gets the code hash of the account at the given address. + +```rust +pub fn code_hash(&self, account: Address) -> B256 +``` + +**Equivalent to**: Solidity's `EXTCODEHASH` opcode + +**Example:** + +```rust +#[public] +impl Contract { + pub fn verify_code(&self, account: Address, expected_hash: B256) -> bool { + self.vm().code_hash(account) == expected_hash + } +} +``` + +**Note:** The code hash of an account without code will be the empty hash: `keccak("") = c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. + +## Gas and Metering + +Methods for accessing gas and ink metering information. + +### `evm_gas_left()` + +Gets the amount of gas left after paying for the cost of this hostio. + +```rust +pub fn evm_gas_left(&self) -> u64 +``` + +**Equivalent to**: Solidity's `gasleft()` + +**Example:** + +```rust +use stylus_sdk::call::Call; + +#[public] +impl Contract { + pub fn complex_operation(&mut self, target: ITarget) { + let gas_before = self.vm().evm_gas_left(); + + // Use half the remaining gas for external call + let config = Call::new_mutating(self) + .gas(gas_before / 2); + + target.do_work(self.vm(), config); + } +} +``` + +### `evm_ink_left()` + +Gets the amount of ink remaining after paying for the cost of this hostio. + +```rust +pub fn evm_ink_left(&self) -> u64 +``` + +**Description:** Returns remaining computation units in "ink". See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/gas-metering) for more information on Stylus's compute pricing. + +**Example:** + +```rust +#[public] +impl Contract { + pub fn check_ink(&self) -> u64 { + self.vm().evm_ink_left() + } +} +``` + +### `ink_to_gas(ink)` + +Computes the units of gas per a specified amount of ink. + +```rust +pub fn ink_to_gas(&self, ink: u64) -> u64 +``` + +**Example:** + +```rust +#[public] +impl Contract { + pub fn convert_ink_to_gas(&self, ink: u64) -> u64 { + self.vm().ink_to_gas(ink) + } +} +``` + +### `gas_to_ink(gas)` + +Computes the units of ink per a specified amount of gas. + +```rust +pub fn gas_to_ink(&self, gas: u64) -> u64 +``` + +**Example:** + +```rust +#[public] +impl Contract { + pub fn convert_gas_to_ink(&self, gas: u64) -> u64 { + self.vm().gas_to_ink(gas) + } +} +``` + +## Cryptographic Functions + +The SDK provides cryptographic utilities through the `crypto` module and VM methods. + +### `keccak()` + +Efficiently computes the keccak256 hash of the given preimage. + +```rust +use stylus_sdk::crypto; + +pub fn keccak>(bytes: T) -> B256 +``` + +**Equivalent to**: Solidity's `keccak256()` + +**Example:** + +```rust +use stylus_sdk::crypto; +use alloy_primitives::{Address, FixedBytes, U256}; + +#[public] +impl Contract { + pub fn hash_data(&self, data: Vec) -> FixedBytes<32> { + crypto::keccak(data) + } + + pub fn verify_hash(&self, data: Vec, expected: FixedBytes<32>) -> bool { + crypto::keccak(data) == expected + } + + // Hash multiple values together + pub fn hash_packed(&self, addr: Address, amount: U256) -> FixedBytes<32> { + let packed = [ + addr.as_ref(), + &amount.to_be_bytes_vec(), + ].concat(); + crypto::keccak(packed) + } +} +``` + +### `native_keccak256()` + +VM method for computing keccak256 hash (alternative to `crypto::keccak`). + +```rust +pub fn native_keccak256(&self, input: &[u8]) -> B256 +``` + +**Example:** + +```rust +#[public] +impl Contract { + pub fn hash_via_vm(&self, data: Vec) -> B256 { + self.vm().native_keccak256(&data) + } +} +``` + +**Note:** `crypto::keccak()` is the recommended approach as it's more ergonomic. + +## Event Logging + +Methods for emitting events to the blockchain. + +### `log(event)` + +Emits a typed Solidity event. + +```rust +pub fn log(&self, event: T) +``` + +**Example:** + +```rust +use alloy_sol_types::sol; + +sol! { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +#[public] +impl Token { + pub fn transfer(&mut self, to: Address, value: U256) -> bool { + let from = self.vm().msg_sender(); + + // Transfer logic... + self._transfer(from, to, value); + + // Emit event + self.vm().log(Transfer { from, to, value }); + + true + } +} +``` + +### `raw_log(topics, data)` + +Emits a raw log with custom topics and data. + +```rust +pub fn raw_log(&self, topics: &[B256], data: &[u8]) -> Result<(), &'static str> +``` + +**Example:** + +```rust +use alloy_primitives::{B256, FixedBytes}; + +#[public] +impl Contract { + pub fn emit_custom_log(&self) { + let topic = B256::from([1u8; 32]); + let topics = &[topic]; + let data = b"custom data"; + + self.vm().raw_log(topics, data).unwrap(); + } +} +``` + +**Note:** Maximum of 4 topics allowed. The first topic is typically the event signature hash. + +## Storage Operations + +Methods for interacting with contract storage. + +### `storage_load_bytes32(key)` + +Reads a 32-byte value from permanent storage. + +```rust +pub fn storage_load_bytes32(&self, key: U256) -> B256 +``` + +**Equivalent to**: Solidity's `SLOAD` opcode + +**Note:** Storage is cached for efficiency. Use the SDK's storage types instead of direct storage access. + +### `flush_cache(clear)` + +Persists dirty values in the storage cache to the EVM state trie. + +```rust +pub fn flush_cache(&self, clear: bool) +``` + +**Parameters:** + +- `clear`: If `true`, drops the cache entirely after flushing + +**Note:** Typically handled automatically by the SDK. Manual cache flushing is rarely needed. + +## Memory Management + +### `pay_for_memory_grow(pages)` + +Pays for memory growth in WASM pages. + +```rust +pub fn pay_for_memory_grow(&self, pages: u16) +``` + +**Note:** The `#[entrypoint]` macro handles this automatically. Manual calls are not recommended and will unproductively consume gas. + +## Complete Example + +Here's a comprehensive example using various global variables and functions: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloy_primitives::{Address, U256, FixedBytes}; +use alloy_sol_types::sol; +use stylus_sdk::{crypto, prelude::*}; + +sol! { + event Action( + address indexed caller, + uint256 value, + uint256 timestamp, + bytes32 data_hash + ); +} + +sol_storage! { + #[entrypoint] + pub struct ContextExample { + mapping(address => uint256) balances; + uint256 total_deposits; + uint256 creation_block; + address owner; + } +} + +#[public] +impl ContextExample { + #[constructor] + pub fn constructor(&mut self) { + // Use tx_origin for factory deployments + self.owner.set(self.vm().tx_origin()); + self.creation_block.set(U256::from(self.vm().block_number())); + } + + #[payable] + pub fn deposit(&mut self, data: Vec) { + // Message context + let caller = self.vm().msg_sender(); + let amount = self.vm().msg_value(); + + // Block context + let timestamp = self.vm().block_timestamp(); + let block_num = self.vm().block_number(); + + // Require minimum value + if amount < U256::from(1000) { + panic!("Insufficient deposit"); + } + + // Update balances + let balance = self.balances.get(caller); + self.balances.setter(caller).set(balance + amount); + self.total_deposits.set(self.total_deposits.get() + amount); + + // Hash the data + let data_hash = crypto::keccak(&data); + + // Emit event + self.vm().log(Action { + caller, + value: amount, + timestamp: U256::from(timestamp), + data_hash, + }); + } + + pub fn get_contract_info(&self) -> (Address, U256, u64, u64) { + ( + self.vm().contract_address(), // Contract's address + self.vm().balance(self.vm().contract_address()), // Contract's balance + self.vm().chain_id(), // Chain ID + self.vm().block_number(), // Current block + ) + } + + pub fn verify_signature(&self, message: Vec, expected_hash: FixedBytes<32>) -> bool { + let hash = crypto::keccak(message); + hash == expected_hash + } + + pub fn is_owner(&self, account: Address) -> bool { + account == self.owner.get() + } + + pub fn time_since_creation(&self) -> u64 { + let current_block = self.vm().block_number(); + let creation_block: u64 = self.creation_block.get().try_into().unwrap_or(0); + current_block.saturating_sub(creation_block) + } +} +``` + +## Summary of Available Methods + +### Message Context + +- `msg_sender()` → `Address` - Caller's address +- `msg_value()` → `U256` - ETH sent with call +- `msg_reentrant()` → `bool` - Is reentrant call + +### Transaction Context + +- `tx_origin()` → `Address` - Original transaction sender +- `tx_gas_price()` → `U256` - Gas price +- `tx_ink_price()` → `u32` - Ink price + +### Block Context + +- `block_number()` → `u64` - Block number +- `block_timestamp()` → `u64` - Block timestamp +- `block_basefee()` → `U256` - Base fee +- `block_coinbase()` → `Address` - Batch poster +- `block_gas_limit()` → `u64` - Gas limit + +### Chain Context + +- `chain_id()` → `u64` - Chain identifier + +### Account Information + +- `contract_address()` → `Address` - This contract's address +- `balance(Address)` → `U256` - Account balance +- `code(Address)` → `Vec` - Account code +- `code_size(Address)` → `usize` - Code size +- `code_hash(Address)` → `B256` - Code hash + +### Gas and Metering + +- `evm_gas_left()` → `u64` - Remaining gas +- `evm_ink_left()` → `u64` - Remaining ink +- `ink_to_gas(u64)` → `u64` - Convert ink to gas +- `gas_to_ink(u64)` → `u64` - Convert gas to ink + +### Cryptographic Functions + +- `crypto::keccak(bytes)` → `B256` - Keccak256 hash +- `native_keccak256(&[u8])` → `B256` - Keccak256 via VM + +### Event Logging + +- `log(event)` - Emit typed event +- `raw_log(&[B256], &[u8])` - Emit raw log + +## See Also + +- [Contracts](./contracts.mdx) - Contract structure and methods +- [Primitives](./data-types/primitives.mdx) - Basic data types +- [Storage Types](./data-types/storage.mdx) - Persistent storage diff --git a/docs/stylus/fundamentals/prerequisites.mdx b/docs/stylus/fundamentals/prerequisites.mdx new file mode 100644 index 0000000000..225fc8df88 --- /dev/null +++ b/docs/stylus/fundamentals/prerequisites.mdx @@ -0,0 +1,63 @@ +--- +title: 'Prerequisites and setup' +sidebar_label: 'Prerequisites' +description: 'Set up your development environment for Stylus' +user_story: 'As a new Stylus developer, I want to set up my development environment' +content_type: how-to +author: offchainlabs +sme: offchainlabs +sidebar_position: 1 +--- + +import RustSetup from '../partials/_setup-rust-toolchain.mdx'; +import CargoStylusSetup from '../partials/_setup-cargo-stylus.mdx'; +import DockerSetup from '../partials/_setup-docker-nitro.mdx'; + +# Prerequisites and setup + +This guide will help you set up everything you need to develop Stylus contracts. + +## System requirements + +- **Operating System**: macOS, Linux, or Windows (WSL2) +- **RAM**: Minimum 8GB, 16GB recommended +- **Disk Space**: At least 20GB free + +## Install Rust + + + +## Install cargo-stylus CLI + + + +## Install Docker (for local testnet) + + + +## Verify installation + +```shell +# Check Rust version +rustc --version + +# Check cargo-stylus +cargo stylus --version + +# Check Docker +docker --version +``` + +Expected output: + +``` +rustc 1.75.0 (or higher) +cargo-stylus 0.x.x +Docker version 24.0.0 (or higher) +``` + +## Next steps + +- [Create your first project](/stylus/quickstart) +- [Understand project structure](/stylus/fundamentals/project-structure) +- [Learn about contracts](/stylus/fundamentals/contracts) diff --git a/docs/stylus/reference/project-structure.mdx b/docs/stylus/fundamentals/project-structure.mdx similarity index 93% rename from docs/stylus/reference/project-structure.mdx rename to docs/stylus/fundamentals/project-structure.mdx index c37969b3d7..5fe01e06cc 100644 --- a/docs/stylus/reference/project-structure.mdx +++ b/docs/stylus/fundamentals/project-structure.mdx @@ -1,11 +1,11 @@ --- -title: 'Structure of a Rust Contract' +title: 'Structure of a Stylus Rust project' description: 'A quick overview of how contracts are structured with the Stylus Rust SDK' author: chrisco sme: chrisco sidebar_position: 1 target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- Contracts in Rust are similar to contracts in Solidity. Each contract can contain declarations of State Variables, Functions, Function Modifiers, Events, Errors, Struct Types, and Enum Types. In addition, Rust contracts can import third-party packages from [crates.io](https://crates.io) as dependencies and use them for advanced functionality. @@ -24,7 +24,7 @@ In the most basic example, this is how a Rust contract will be organized. The si `src/lib.rs` is the root module of your contract's code. Here, you can import utilities or methods from internal or external modules, define the data layout of your contract's state variables, and define your contract's public API. This module must define a root data struct with the `#[entrypoint]` macro and provide an impl block annotated with `#[public]` to define public or external methods. See [First App](https://stylus-by-example.org/basic_examples/first_app) for an example of this. These macros are used to maintain [Solidity ABI](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#basic-design) compatibility to ensure that Rust contracts work with existing Solidity libraries and tooling. -`src/main.rs` is typically auto-generated by [cargo-stylus](https://github.com/OffchainLabs/cargo-stylus) and does not usually need to be modified. Its purpose is to assist with the generation of [JSON describing](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#json) your contract's public interface, for use with automated tooling and frontend frameworks. +`src/main.rs` is typically auto-generated by [cargo-stylus](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) and does not usually need to be modified. Its purpose is to assist with the generation of [JSON describing](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#json) your contract's public interface, for use with automated tooling and frontend frameworks. `Cargo.toml` is a standard file that Rust projects use to define a package's name, repository location, etc, as well as import dependencies and define feature and build flags. From here, you can define required dependencies such as the [Stylus SDK](https://crates.io/crates/stylus-sdk) itself or import third-party packages from [crates.io](https://crates.io). See [First Steps with Cargo](https://doc.rust-lang.org/cargo/getting-started/first-steps.html) if you are new to Rust. @@ -159,7 +159,7 @@ Use imported types in your contract: let price = dec!(72.00); ``` -Note, not all Rust crates are compatible with Stylus since they need to be compiled to WASM and used in a blockchain context, which is more limited than a desktop application. For instance, the `rand` crate is not usable, as there is no onchain randomness available to smart contracts. In addition, contracts cannot access functions that use networking or filesystem access features. There is also a need to be mindful of the size of the crates you import, since the default contract size limit is 24KB (compressed). Crates that do not use the standard library (`no_std` crates) tend to work best. See [Using public Rust crates](https://docs.arbitrum.io/stylus/recommended-libraries#using-public-rust-crates) for more important details on using public Rust crates as well as a curated list of crates that tend to work well for smart contract development. +Note, not all Rust crates are compatible with Stylus since they need to be compiled to WASM and used in a blockchain context, which is more limited than a desktop application. For instance, the `rand` crate is not usable, as there is no onchain randomness available to smart contracts. In addition, contracts cannot access functions that use networking or filesystem access features. There is also a need to be mindful of the size of the crates you import, since the default contract size limit is 24KB (compressed). Crates that do not use the standard library (`no_std` crates) tend to work best. See [Using public Rust crates](https://docs.arbitrum.io/stylus/advanced/recommended-libraries#using-public-rust-crates) for more important details on using public Rust crates as well as a curated list of crates that tend to work well for smart contract development. ## Events diff --git a/docs/stylus/how-tos/testing-contracts.mdx b/docs/stylus/fundamentals/testing-contracts.mdx similarity index 99% rename from docs/stylus/how-tos/testing-contracts.mdx rename to docs/stylus/fundamentals/testing-contracts.mdx index dd61568bb5..17ddda113a 100644 --- a/docs/stylus/how-tos/testing-contracts.mdx +++ b/docs/stylus/fundamentals/testing-contracts.mdx @@ -1,11 +1,11 @@ --- id: 'testing-contracts' -title: 'Testing Smart Contracts with Stylus' +title: 'Testing smart contracts with Stylus' description: 'A comprehensive guide to writing and running tests for Stylus smart contracts.' sme: anegg0 target_audience: 'Developers writing smart contracts using Stylus.' sidebar_position: 3 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/gentle-introduction.mdx b/docs/stylus/gentle-introduction.mdx index a4bb3621ad..85b08c7299 100644 --- a/docs/stylus/gentle-introduction.mdx +++ b/docs/stylus/gentle-introduction.mdx @@ -1,25 +1,27 @@ --- id: gentle-introduction title: 'A gentle introduction to Stylus' -description: 'An introduction to Stylus, which enables writing EVM-compatible smart contracts in programming languages that compile to WASM, such as Rust, C, and C++.' +description: 'An introduction to Stylus, which enables writing EVM-compatible smart contracts in programming languages that compile to WASM, such as Rust, C, and C++' +user_story: 'As a developer, I want to understand what Stylus is and why I should use it' +content_type: concept author: amarrazza sme: amarrazza target_audience: 'Developers who want to build on Arbitrum using popular programming languages, like Rust' sidebar_position: 1 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; ### In a nutshell: -- Stylus lets you write smart contracts in programming languages that compile to WASM, such as Rust, C, C++, and others, allowing you to use their ecosystem of libraries and tools. Language and tooling support exists for Rust. You can try the SDK and CLI with the [quickstart](/stylus/quickstart.mdx). +- Stylus lets you write smart contracts in programming languages that compile to WASM, such as Rust, C, C++, and others, allowing you to use their ecosystem of libraries and tools. Language and tooling support exists for Rust. You can try the SDK and CLI with the [quickstart](/stylus/quickstart). - Solidity contracts and Stylus contracts are interoperable. In Solidity, you can call a Rust program and vice versa, thanks to a second, coequal WASM virtual machine. - Stylus contracts offer reduced gas costs for memory and compute-intensive operations because WASM programs execute more efficiently than EVM bytecode for these workloads. ### What's Stylus? -Stylus is an upgrade to Arbitrum Nitro [(ArbOS 32)](/run-arbitrum-node/arbos-releases/arbos32.mdx), the tech stack powering Arbitrum One, Arbitrum Nova, and Arbitrum chains. This upgrade adds a second, coequal virtual machine to the EVM, where EVM contracts continue to behave exactly as they would in Ethereum. This paradigm is called MultiVM because it is additive to existing functionality. +Stylus is an upgrade to Arbitrum Nitro [(ArbOS 32)](/run-arbitrum-node/arbos-releases/arbos32), the tech stack powering Arbitrum One, Arbitrum Nova, and Arbitrum chains. This upgrade adds a second, coequal virtual machine to the EVM, where EVM contracts continue to behave exactly as they would in Ethereum. This paradigm is called MultiVM because it is additive to existing functionality. @@ -35,7 +37,7 @@ Bringing a Stylus program to life involves four stages: coding, activation, exec You write your smart contract in any programming language that compiles to WASM. Rust has the most developed support with an open-source SDK for smart contract development. C and C++ are also supported, so that you can deploy existing contracts in those languages onchain with minimal modifications. -The [Stylus SDK for Rust](/stylus/reference/rust-sdk-guide.md) provides the development framework and language features for smart contract development. It also provides access to EVM-specific functionality used by smart contract developers. +The [Stylus SDK for Rust](/stylus/reference/rust-sdk-guide) provides the development framework and language features for smart contract development. It also provides access to EVM-specific functionality used by smart contract developers. #### Activation @@ -46,7 +48,7 @@ To make your contract callable, it must undergo an activation process. During ac Stylus measures computational costs using ink instead of gas. Ink works like gas but is thousands of times smaller. WASM executes faster than the EVM, so a single EVM operation takes as long as thousands of WASM operations. A finer-grained unit makes pricing more precise. :::note -Stylus contracts need to be reactivated once per year (365 days) or after any Stylus upgrade. You can do this using [`cargo-stylus`](/stylus/using-cli.mdx#cargo-stylus-commands-reference) or the [ArbWasm precompile](/build-decentralized-apps/precompiles/reference#common-precompiles). If a contract isn't reactivated, it becomes uncallable. +Stylus contracts need to be reactivated once per year (365 days) or after any Stylus upgrade. You can do this using [`cargo-stylus`](/stylus/cli-tools/commands-reference) or the [ArbWasm precompile](/build-decentralized-apps/precompiles/reference#common-precompiles). If a contract isn't reactivated, it becomes uncallable. ::: #### Execution @@ -72,7 +74,7 @@ Stylus can integrate into existing Solidity projects by calling a Stylus contrac ### Getting started -1. Follow the [quickstart](/stylus/quickstart.mdx) to deploy your first Stylus contract, and explore the [Rust SDK](/stylus/reference/overview.md) documentation. +1. Follow the [quickstart](/stylus/quickstart) to deploy your first Stylus contract, and explore the [Rust SDK](/stylus/reference/overview) documentation. 2. Join the Stylus Developer [Telegram](https://t.me/arbitrum_stylus) group and [Arbitrum Discord](https://discord.gg/arbitrum) for community support. 3. Browse the [Awesome Stylus](https://github.com/OffchainLabs/awesome-stylus) repository for community-contributed projects, examples, and tools. 4. Subscribe to the [Stylus Saturdays](https://stylus-saturdays.com/) newsletter for tutorials and technical content. diff --git a/docs/stylus/guides/_category_.yml b/docs/stylus/guides/_category_.yml new file mode 100644 index 0000000000..451487c28c --- /dev/null +++ b/docs/stylus/guides/_category_.yml @@ -0,0 +1,5 @@ +label: 'Guides' +position: 3 +link: + type: generated-index + description: Practical guides for common Stylus development tasks. diff --git a/docs/stylus/how-tos/adding-support-for-new-languages.mdx b/docs/stylus/guides/adding-support-for-new-languages.mdx similarity index 96% rename from docs/stylus/how-tos/adding-support-for-new-languages.mdx rename to docs/stylus/guides/adding-support-for-new-languages.mdx index 03f167dc7b..346d42abfe 100644 --- a/docs/stylus/how-tos/adding-support-for-new-languages.mdx +++ b/docs/stylus/guides/adding-support-for-new-languages.mdx @@ -6,14 +6,14 @@ sme: rauljordan target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- [Arbitrum Stylus](../gentle-introduction.mdx) is a new technology developed for Arbitrum chains which gives smart contract developers superpowers. With Stylus, developers can write EVM-compatible smart contracts in many different programming languages, and reap massive performance gains. Stylus slashes fees, with performance gains ranging from 10-70x, and memory efficiency gains as high as 100-500x. This is possible thanks to [WebAssembly](https://www.infoworld.com/article/3291780/what-is-webassembly-the-next-generation-web-platform-explained.html) technology, which all Stylus contracts compile to. Stylus smart contracts live under the **same Ethereum state trie** in Arbitrum nodes, and can fully interoperate with Solidity or Vyper EVM smart contracts. With Stylus, developers can write smart contracts in Rust that talk to Solidity and vice versa without any limitations. -Today, the Stylus testnet also comes with two officially supported [SDKs](/stylus/overview.mdx) for developers to write contracts in the [Rust](../reference/rust-sdk-guide.md) or [C](https://github.com/OffchainLabs/stylus-sdk-c) programming languages. +Today, the Stylus testnet also comes with two officially supported SDKs for developers to write contracts in the [Rust](../reference/rust-sdk-guide.md) or [C](https://github.com/OffchainLabs/stylus-sdk-c) programming languages. However, _anyone_ can add support for new languages in Stylus. **As long as a programming language can compile to WebAssembly**, Stylus will let you use it to write EVM-compatible smart contracts. Note that in order to be deployed onchain, your compiled program must fit under the 24Kb brotli-compressed limit, and should meet Stylus gas metering requirements. @@ -30,7 +30,7 @@ Programs written in Zig and deployed to Stylus have a tiny footprint and will ha ## Requirements - Download and install [Zig 0.11.0](https://ziglang.org/downloads) -- Install [Rust](https://www.rust-lang.org/tools/install), which we'll need for the [Stylus CLI tool](https://github.com/OffchainLabs/cargo-stylus) to deploy our program to the Stylus testnet +- Install [Rust](https://www.rust-lang.org/tools/install), which we'll need for the [Stylus CLI tool](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) to deploy our program to the Stylus testnet We'll also be using Rust to run an example script that can call our Zig contract on the Stylus testnet using the popular [ethers-rs](https://github.com/gakonst/ethers-rs) library. @@ -79,7 +79,7 @@ Next, we can build our Zig library to a freestanding WASM file for our onchain d zig build-lib ./src/main.zig -target wasm32-freestanding -dynamic --export=user_entrypoint -OReleaseSmall --export=mark_unused ``` -This is enough for us to deploy on the Stylus testnet! We'll use the [Stylus CLI tool](https://github.com/OffchainLabs/cargo-stylus), which you installed earlier using `cargo install`: +This is enough for us to deploy on the Stylus testnet! We'll use the [Stylus CLI tool](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus), which you installed earlier using `cargo install`: ``` cargo stylus deploy --private-key= --wasm-file=main.wasm diff --git a/docs/stylus/how-tos/caching-contracts.mdx b/docs/stylus/guides/caching-contracts.mdx similarity index 99% rename from docs/stylus/how-tos/caching-contracts.mdx rename to docs/stylus/guides/caching-contracts.mdx index 92c3a75094..cd55d7d5c7 100644 --- a/docs/stylus/how-tos/caching-contracts.mdx +++ b/docs/stylus/guides/caching-contracts.mdx @@ -5,7 +5,7 @@ description: 'A conceptual overview of the Stylus caching strategy and CacheMana sme: mahsa-moosavi target_audience: 'Developers deploying smart contracts using Stylus.' sidebar_position: 3 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- Stylus is designed for fast computation and efficiency. However, diff --git a/docs/stylus/guides/deploying-non-rust-wasm-contracts.mdx b/docs/stylus/guides/deploying-non-rust-wasm-contracts.mdx new file mode 100644 index 0000000000..fc3e5a7db4 --- /dev/null +++ b/docs/stylus/guides/deploying-non-rust-wasm-contracts.mdx @@ -0,0 +1,803 @@ +--- +id: deploying-non-rust-wasm-contracts +title: 'Deploying non-Rust WASM contracts' +description: 'Deploy WebAssembly contracts from C, C++, and other languages to Arbitrum Stylus' +author: chrisco +sme: chrisco +target_audience: 'Developers who need to deploy non-Rust WASM contracts' +content_type: how-to +sidebar_position: 3 +displayed_sidebar: buildStylusSidebar +--- + +While Rust provides the best developer experience for Stylus, any language that compiles to WebAssembly can be deployed. This guide explains how to deploy WASM contracts written in C, C++, or even pure WebAssembly Text (WAT). + +## Overview + +Stylus accepts any valid WebAssembly module that meets its requirements. You can: + +- Write contracts in **C or C++** using the Stylus C SDK +- Use **WebAssembly Text (WAT)** for direct bytecode control +- Compile from **any language** that targets `wasm32-unknown-unknown` +- Deploy **pre-compiled WASM** binaries directly + +The key is using the `--wasm-file` flag with `cargo stylus` commands to bypass Rust compilation. + +## Why use non-Rust languages? + +Different languages excel at different tasks: + +| Language | Best For | Use Cases | +| ------------------ | -------------------------------------- | -------------------------------------------------- | +| **C/C++** | Low-level control, cryptography | Hash functions, signature verification, algorithms | +| **WAT** | Learning, debugging, minimal contracts | Simple logic, educational examples | +| **AssemblyScript** | TypeScript developers | Web3 integration with familiar syntax | +| **Other** | Specific requirements | Domain-specific computations | + +### When to choose non-Rust + +- **Existing codebase**: Port existing C/C++ cryptography libraries +- **Performance-critical**: Hand-optimized assembly-like control +- **Minimal size**: Ultra-compact contracts for specific operations +- **Team expertise**: Leverage existing C/C++ knowledge + +### When to stick with Rust + +- **Full-featured contracts**: Complex DeFi, NFTs, governance +- **Type safety**: Strong guarantees and tooling +- **Ecosystem**: Rich library support and examples +- **Productivity**: Higher-level abstractions and macros + +## WASM requirements + +All WASM modules deployed to Stylus must meet these requirements: + +### Required exports + +```wasm +(export "user_entrypoint" (func $user_entrypoint)) +(export "memory" (memory 0)) +``` + +The `user_entrypoint` function: + +- **Signature**: `(param i32) (result i32)` +- **Parameter**: Length of input calldata in bytes +- **Returns**: Length of output data in bytes + +### Allowed imports + +Only functions from the `vm_hooks` module are permitted: + +```wasm +(import "vm_hooks" "msg_sender" (func $msg_sender (param i32))) +(import "vm_hooks" "storage_load_bytes32" (func $storage_load (param i32 i32))) +(import "vm_hooks" "storage_store_bytes32" (func $storage_store (param i32 i32))) +``` + +See the [hostio exports documentation](/stylus/advanced/hostio-exports) for the complete list of available VM hooks. + +### Memory requirements + +- Linear memory must be exported as `"memory"` +- Memory growth must be explicitly paid for +- Initial memory size should be minimal (often `0 0`) +- Maximum memory is limited by gas costs + +### Compilation target + +- **Target triple**: `wasm32-unknown-unknown` +- **No standard library**: WASM runs in a sandboxed environment +- **No floating point**: Not yet supported by Stylus +- **No SIMD**: Not yet supported by Stylus +- **No reference types**: Disabled for compatibility + +## WebAssembly Text (WAT) + +WAT provides direct control over WASM bytecode using human-readable text format. + +### Minimal contract + +The simplest valid Stylus contract: + +```wasm +(module + ;; Export linear memory + (memory 0 0) + (export "memory" (memory 0)) + + ;; Required entrypoint + ;; Takes calldata length, returns output length + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) ;; Return 0 bytes + )) +``` + +Save as `minimal.wat` and deploy: + +```shell +cargo stylus deploy --wasm-file=minimal.wat --private-key-path=./key.txt +``` + +### Echo contract + +Returns input data unchanged: + +```wasm +(module + (memory 1 1) + (export "memory" (memory 0)) + + ;; Import VM hook to read calldata + (import "vm_hooks" "read_args" (func $read_args (param i32))) + + (func (export "user_entrypoint") (param $args_len i32) (result i32) + ;; Read calldata into memory at offset 0 + (call $read_args (i32.const 0)) + + ;; Return the same length (echo) + (local.get $args_len) + )) +``` + +### Storage counter + +Increment a value in storage: + +```wasm +(module + (memory 1 1) + (export "memory" (memory 0)) + + ;; Import storage operations + (import "vm_hooks" "storage_load_bytes32" + (func $storage_load (param i32 i32))) + (import "vm_hooks" "storage_store_bytes32" + (func $storage_store (param i32 i32))) + + (func (export "user_entrypoint") (param $args_len i32) (result i32) + ;; Load current value from storage slot 0 + (call $storage_load + (i32.const 0) ;; key pointer + (i32.const 32)) ;; value destination + + ;; Increment the value at memory[32] + (i32.store (i32.const 32) + (i32.add + (i32.load (i32.const 32)) + (i32.const 1))) + + ;; Store back to storage + (call $storage_store + (i32.const 0) ;; key pointer + (i32.const 32)) ;; value pointer + + ;; Return 0 bytes of output + (i32.const 0) + )) +``` + +### Checking WAT contracts + +Validate before deploying: + +```shell +cargo stylus check --wasm-file=counter.wat +``` + +Output shows validation results: + +``` +Reading WASM file at counter.wat +Compressed WASM size: 142 B +Contract succeeded Stylus onchain activation checks with Stylus version: 1 +``` + +## C/C++ development + +The [Stylus C SDK](https://github.com/OffchainLabs/stylus-sdk-c) enables C/C++ smart contract development. + +### Installation + +Install the C SDK: + +```shell +git clone https://github.com/OffchainLabs/stylus-sdk-c.git +cd stylus-sdk-c +``` + +Install dependencies: + +```shell +# macOS +brew install llvm binaryen wabt + +# Ubuntu/Debian +sudo apt-get install clang lld wasm-ld binaryen wabt +``` + +### Project structure + +Basic C project layout: + +``` +my-contract/ +├── Makefile +├── src/ +│ └── main.c +└── include/ + └── stylus_sdk.h +``` + +### Simple C contract + +```c +// main.c +#include "stylus_sdk.h" + +// Storage slot for counter +static uint8_t counter_slot[32] = {0}; + +// Main entrypoint +int main(int argc, char *argv[]) { + // Load counter from storage + uint8_t value[32]; + storage_load_bytes32(counter_slot, value); + + // Increment + value[31]++; + + // Store back + storage_store_bytes32(counter_slot, value); + + return 0; +} +``` + +### C SDK features + +The C SDK provides: + +```c +// Account operations +void msg_sender(uint8_t *sender); +void tx_origin(uint8_t *origin); +void contract_address(uint8_t *addr); + +// Storage operations +void storage_load_bytes32(uint8_t *key, uint8_t *dest); +void storage_store_bytes32(uint8_t *key, uint8_t *value); + +// Block information +uint64_t block_timestamp(void); +uint64_t block_number(void); +void block_basefee(uint8_t *basefee); + +// Call operations +void call_contract( + uint8_t *contract, + uint8_t *calldata, + uint32_t calldata_len, + uint8_t *value, + uint32_t gas, + uint8_t *return_data_len +); + +// And many more... +``` + +### Building C contracts + +Create a Makefile: + +```makefile +CLANG = clang +WASM_LD = wasm-ld +WASM_OPT = wasm-opt + +CFLAGS = -target wasm32 -nostdlib -O3 +LDFLAGS = -no-entry --export=user_entrypoint --export=memory + +SRC = src/main.c +OUT = build/contract.wasm +OUT_OPT = build/contract-opt.wasm + +all: $(OUT_OPT) + +$(OUT): $(SRC) + mkdir -p build + $(CLANG) $(CFLAGS) -c $(SRC) -o build/main.o + $(WASM_LD) $(LDFLAGS) build/main.o -o $(OUT) + +$(OUT_OPT): $(OUT) + $(WASM_OPT) -Oz $(OUT) -o $(OUT_OPT) + +clean: + rm -rf build + +deploy: $(OUT_OPT) + cargo stylus deploy --wasm-file=$(OUT_OPT) \ + --private-key-path=$$PRIVATE_KEY_PATH + +check: $(OUT_OPT) + cargo stylus check --wasm-file=$(OUT_OPT) +``` + +Build and deploy: + +```shell +make +make check +make deploy +``` + +### C cryptography example + +Verifying a signature: + +```c +#include "stylus_sdk.h" +#include + +// Verify ECDSA signature +int verify_signature( + uint8_t *message_hash, + uint8_t *signature, + uint8_t *public_key +) { + uint8_t recovered[65]; + + // Recover signer from signature + if (ecrecover(message_hash, signature, recovered) != 0) { + return -1; // Recovery failed + } + + // Compare with expected public key + if (memcmp(recovered + 1, public_key, 64) == 0) { + return 0; // Valid signature + } + + return -1; // Invalid signature +} + +int main(int argc, char *argv[]) { + uint8_t msg_hash[32]; + uint8_t sig[65]; + uint8_t pubkey[64]; + + // Read inputs from calldata + read_args(0); + memcpy(msg_hash, memory, 32); + memcpy(sig, memory + 32, 65); + memcpy(pubkey, memory + 97, 64); + + // Verify + int result = verify_signature(msg_hash, sig, pubkey); + + // Write result + memory[0] = (result == 0) ? 1 : 0; + write_result(memory, 1); + + return 0; +} +``` + +## AssemblyScript contracts + +AssemblyScript is TypeScript-like language that compiles to WebAssembly. + +### Installation + +```shell +npm install -g assemblyscript +npm install @assemblyscript/loader +``` + +### Simple AssemblyScript contract + +```typescript +// contract.ts + +// Import Stylus VM hooks +@external("vm_hooks", "msg_sender") +declare function msg_sender(ptr: usize): void; + +@external("vm_hooks", "storage_load_bytes32") +declare function storage_load(key: usize, dest: usize): void; + +@external("vm_hooks", "storage_store_bytes32") +declare function storage_store(key: usize, value: usize): void; + +// Storage key +const COUNTER_KEY: StaticArray = [0, 0, 0, 0, /* ... 32 zeros ... */]; + +// Entrypoint +export function user_entrypoint(args_len: i32): i32 { + // Load counter + let value = new StaticArray(32); + storage_load( + changetype(COUNTER_KEY), + changetype(value) + ); + + // Increment + value[31]++; + + // Store + storage_store( + changetype(COUNTER_KEY), + changetype(value) + ); + + return 0; // No output +} +``` + +### Compile AssemblyScript + +```shell +asc contract.ts \ + --target release \ + --exportRuntime \ + --exportTable \ + -o contract.wasm +``` + +### Deploy AssemblyScript contract + +```shell +cargo stylus deploy \ + --wasm-file=contract.wasm \ + --private-key-path=./key.txt +``` + +## Deployment workflow + +### 1. Prepare your WASM + +Ensure your WASM module meets requirements: + +```shell +# Check WASM structure with wasm-objdump +wasm-objdump -x contract.wasm | grep -A 5 "Export\|Import" + +# Should show: +# Export[0]: +# - func[0] +# - memory[0] +# Import[0]: +# - module="vm_hooks" func=... +``` + +### 2. Optimize the WASM + +Reduce size with wasm-opt: + +```shell +wasm-opt -Oz contract.wasm -o contract-opt.wasm +``` + +### 3. Check before deploying + +Validate the contract: + +```shell +cargo stylus check --wasm-file=contract-opt.wasm +``` + +### 4. Deploy + +Deploy to testnet: + +```shell +cargo stylus deploy \ + --wasm-file=contract-opt.wasm \ + --private-key-path=./key.txt \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" +``` + +### 5. Verify deployment + +Check deployment succeeded: + +```shell +# Output shows: +Compressed WASM size: 245 B +Deploying contract to address 0x... +Confirmed tx 0x... +Activating contract at address 0x... +Confirmed tx 0x... +``` + +## Best practices + +### 1. Minimize binary size + +```shell +# ✅ Good: Optimize aggressively +wasm-opt -Oz input.wasm -o output.wasm + +# Use wasm-strip to remove symbols +wasm-strip output.wasm + +# Check final size +ls -lh output.wasm +``` + +### 2. Test with cargo stylus check + +```shell +# ✅ Good: Always check before deploying +cargo stylus check --wasm-file=contract.wasm + +# Test on testnet first +cargo stylus deploy \ + --wasm-file=contract.wasm \ + --private-key-path=./key.txt \ + --endpoint="https://sepolia-rollup.arbitrum.io/rpc" +``` + +### 3. Use standard memory layout + +```c +// ✅ Good: Predictable memory layout +uint8_t calldata[1024]; // 0-1023: Input data +uint8_t storage[32]; // 1024-1055: Storage scratch +uint8_t output[256]; // 1056-1311: Output buffer + +// ❌ Bad: Unpredictable allocations +uint8_t *data = malloc(size); // No malloc in WASM! +``` + +### 4. Handle calldata properly + +```wasm +;; ✅ Good: Read calldata into memory +(call $read_args (i32.const 0)) + +;; Process the data +(call $process_calldata (local.get $args_len)) + +;; ❌ Bad: Assume calldata location +(i32.load (i32.const 0)) ;; Calldata not automatically loaded +``` + +### 5. Export all required functions + +```wasm +;; ✅ Good: Export entrypoint and memory +(export "user_entrypoint" (func $main)) +(export "memory" (memory 0)) + +;; ❌ Bad: Missing exports +(export "main" (func $main)) ;; Wrong name! +``` + +### 6. Use VM hooks correctly + +```c +// ✅ Good: Proper VM hook usage +uint8_t sender[20]; +msg_sender(sender); + +// ✅ Good: Check return values +uint8_t success; +call_contract(addr, data, len, value, gas, &success); +if (!success) { + revert("Call failed"); +} + +// ❌ Bad: Ignoring errors +call_contract(addr, data, len, value, gas, NULL); +``` + +### 7. Mind the size limit + +```shell +# Check compressed size +cargo stylus check --wasm-file=contract.wasm + +# Should show: +# Compressed WASM size: < 24 KB + +# If too large: +# - Remove debug symbols +# - Enable aggressive optimization +# - Minimize code and data sections +``` + +## Troubleshooting + +### Missing entrypoint + +**Error**: `WASM is missing the entrypoint export` + +**Solution**: Ensure `user_entrypoint` is exported: + +```wasm +;; WAT +(func (export "user_entrypoint") (param i32) (result i32) + ;; Implementation +) + +// C +int user_entrypoint(int argc) __attribute__((export_name("user_entrypoint"))); +``` + +### Invalid imports + +**Error**: `contract imports unauthorized function` + +**Solution**: Only import from `vm_hooks`: + +```wasm +;; ✅ Allowed +(import "vm_hooks" "msg_sender" (func $msg_sender (param i32))) + +;; ❌ Not allowed +(import "env" "print" (func $print (param i32))) +``` + +### Memory not exported + +**Error**: `WASM must export memory` + +**Solution**: Export linear memory: + +```wasm +;; WAT +(memory 1 1) +(export "memory" (memory 0)) + +// C Makefile +LDFLAGS = -no-entry --export=user_entrypoint --export=memory +``` + +### Size too large + +**Error**: `Compressed WASM exceeds 24KB` + +**Solutions**: + +1. Optimize with wasm-opt: + + ```shell + wasm-opt -Oz input.wasm -o output.wasm + ``` + +2. Strip symbols: + + ```shell + wasm-strip output.wasm + ``` + +3. Remove unused code: + + ```c + // Use static/inline for internal functions + static inline void helper(void) { } + ``` + +4. Minimize data section: + + ```c + // ✅ Good: Minimal data + const uint8_t PREFIX[4] = {0xEF, 0xF0, 0x00, 0x00}; + + // ❌ Bad: Large data + const char *STRINGS[1000] = { /* ... */ }; + ``` + +### Compilation errors + +**Error**: Clang fails to compile + +**Solutions**: + +1. Target wasm32: + + ```shell + clang -target wasm32 -nostdlib -c main.c + ``` + +2. Disable standard library: + + ```c + // Don't use stdio, stdlib, etc. + // Use SDK-provided functions + ``` + +3. Check imports/exports: + ```shell + wasm-objdump -x contract.wasm + ``` + +### Runtime errors + +**Error**: Contract reverts unexpectedly + +**Solutions**: + +1. Check gas usage: + + ```shell + cargo stylus deploy --estimate-gas --wasm-file=contract.wasm + ``` + +2. Add debug output (testnet only): + + ```c + emit_log(error_msg, sizeof(error_msg)); + ``` + +3. Test with minimal input: + ```shell + # Call with empty calldata + cast call $CONTRACT "0x" + ``` + +## Examples repository + +Official examples for different languages: + +- [**Stylus C SDK**](https://github.com/OffchainLabs/stylus-sdk-c) - C/C++ examples +- [**Stylus Bf SDK**](https://github.com/OffchainLabs/stylus-sdk-bf) - Brainfuck educational examples +- [**Awesome Stylus**](https://github.com/OffchainLabs/awesome-stylus) - Community examples + +## Language support matrix + +| Language | Status | SDK | Best Use Case | +| ------------------ | --------------- | -------------------------------------------------------------- | --------------------------- | +| **Rust** | ✅ Production | [stylus-sdk-rs](https://github.com/OffchainLabs/stylus-sdk-rs) | Full-featured contracts | +| **C/C++** | ✅ Production | [stylus-sdk-c](https://github.com/OffchainLabs/stylus-sdk-c) | Cryptography, algorithms | +| **WAT** | ✅ Supported | Manual | Minimal contracts, learning | +| **AssemblyScript** | 🔶 Community | Custom | TypeScript developers | +| **Go** | 🔶 Experimental | TinyGo | Custom applications | +| **Zig** | 🔶 Experimental | Custom | Systems programming | + +## Advanced: custom languages + +To support a new language: + +1. **Compile to wasm32-unknown-unknown** + + ```shell + your-compiler --target=wasm32-unknown-unknown input.src -o output.wasm + ``` + +2. **Export required functions** + + ``` + - user_entrypoint(i32) -> i32 + - memory + ``` + +3. **Import only vm_hooks** + + ``` + - vm_hooks:msg_sender + - vm_hooks:storage_* + - etc. + ``` + +4. **Test and deploy** + ```shell + cargo stylus check --wasm-file=output.wasm + cargo stylus deploy --wasm-file=output.wasm --private-key-path=./key.txt + ``` + +## Resources + +- [Stylus C SDK](https://github.com/OffchainLabs/stylus-sdk-c) +- [WebAssembly specification](https://webassembly.github.io/spec/) +- [WAT format reference](https://webassembly.github.io/spec/core/text/) +- [Cargo Stylus CLI](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) +- [Awesome Stylus](https://github.com/OffchainLabs/awesome-stylus) +- [WASM binary toolkit (wabt)](https://github.com/WebAssembly/wabt) +- [Binaryen optimization tools](https://github.com/WebAssembly/binaryen) + +## Sources + +- [GitHub - OffchainLabs/stylus-sdk-c: C/C++ Smart Contracts on Arbitrum](https://github.com/OffchainLabs/stylus-sdk-c) +- [GitHub - OffchainLabs/awesome-stylus](https://github.com/OffchainLabs/awesome-stylus) +- [GitHub - OffchainLabs/stylus-sdk-rs: Rust Smart Contracts on Arbitrum](https://github.com/OffchainLabs/stylus-sdk-rs) diff --git a/docs/stylus/guides/exporting-abi.mdx b/docs/stylus/guides/exporting-abi.mdx new file mode 100644 index 0000000000..f75a1e2945 --- /dev/null +++ b/docs/stylus/guides/exporting-abi.mdx @@ -0,0 +1,996 @@ +--- +title: 'Exporting ABIs' +description: 'Exporting Solidity ABIs from Stylus contracts' +author: chrisco +sme: chrisco +sidebar_position: 2 +target_audience: Developers who need to understand how to export ABIs from Stylus contracts for integration with front-end applications and other tools. +displayed_sidebar: buildStylusSidebar +--- + +Stylus contracts written in Rust can automatically generate Solidity Application Binary Interfaces (ABIs) that enable seamless interoperability with existing Ethereum tools, front-end libraries, and other smart contracts. + +## What is an ABI? + +An Application Binary Interface (ABI) defines how to interact with a smart contract: + +- **Function signatures**: Names, parameters, and return types +- **Events**: Event definitions and indexed parameters +- **Errors**: Custom error types and parameters +- **Constructor**: Initialization parameters + +ABIs enable: + +- Front-end libraries (ethers.js, web3.js, viem) to interact with contracts +- Solidity contracts to call Rust contracts +- Block explorers to decode transactions +- Development tools to provide type-safe interfaces + +## Overview: ABI Generation + +Stylus contracts generate ABIs through: + +1. **`#[public]` macro**: Annotates public functions +2. **`export-abi` feature**: Enables ABI generation code +3. **`cargo stylus export-abi`**: CLI command to generate output +4. **Solidity interface**: Generated Solidity interface file +5. **JSON format**: Optional JSON ABI for tool integration + +The process is automatic—just annotate your functions with `#[public]` and run the export command. + +## Basic Usage + +### Export Solidity interface + +Generate a Solidity interface for your contract: + +```shell +cargo stylus export-abi +``` + +Output: + +```solidity +/** + * This file was automatically generated by Stylus and represents a Rust program. + * For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs). + */ + +// SPDX-License-Identifier: MIT-OR-APACHE-2.0 +pragma solidity ^0.8.23; + +interface IMyContract { + function getValue() external view returns (uint256); + + function setValue(uint256 new_value) external; + + error Unauthorized(address caller); +} +``` + +### Export to file + +Save the interface to a file: + +```shell +cargo stylus export-abi > IMyContract.sol +``` + +Or specify output path: + +```shell +cargo stylus export-abi --output=./interfaces/IMyContract.sol +``` + +### Export JSON ABI + +Generate JSON format ABI (requires `solc` installed): + +```shell +cargo stylus export-abi --json > abi.json +``` + +Output: + +```json +[ + { + "type": "function", + "name": "getValue", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setValue", + "inputs": [ + { + "name": "new_value", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + } +] +``` + +## Writing ABI-Compatible Contracts + +### Basic contract structure + +Contracts must use the `#[public]` macro to generate ABIs: + +```rust +use stylus_sdk::{alloy_primitives::U256, prelude::*}; + +sol_storage! { + #[entrypoint] + pub struct Counter { + uint256 count; + } +} + +#[public] +impl Counter { + // This function will be included in the ABI + pub fn get_count(&self) -> U256 { + self.count.get() + } + + // This function will also be included + pub fn increment(&mut self) { + let count = self.count.get() + U256::from(1); + self.count.set(count); + } +} +``` + +Generated interface: + +```solidity +interface ICounter { + function getCount() external view returns (uint256); + + function increment() external; +} +``` + +### Function visibility mapping + +Rust function signatures map to Solidity visibility: + +```rust +#[public] +impl MyContract { + // Immutable reference → view function + pub fn read_value(&self) -> U256 { + self.value.get() + } + + // Mutable reference → non-view function + pub fn write_value(&mut self, new_value: U256) { + self.value.set(new_value); + } + + // Pure computation (no self) → pure function + pub fn compute(a: U256, b: U256) -> U256 { + a + b + } +} +``` + +Generated Solidity: + +```solidity +interface IMyContract { + function readValue() external view returns (uint256); + + function writeValue(uint256 new_value) external; + + function compute(uint256 a, uint256 b) external pure returns (uint256); +} +``` + +### Type mapping + +Rust types map to Solidity types automatically: + +| Rust Type | Solidity Type | Example | +| ------------------------- | ------------------------------------- | ---------------------- | +| `U256` | `uint256` | Token amounts | +| `U128`, `u128` | `uint128` | Medium integers | +| `u64`, `u32`, `u16`, `u8` | `uint64`, `uint32`, `uint16`, `uint8` | Small integers | +| `I256` | `int256` | Signed integers | +| `Address` | `address` | Account addresses | +| `bool` | `bool` | Boolean values | +| `FixedBytes` | `bytesN` | Fixed-size byte arrays | +| `Bytes` | `bytes` | Dynamic byte arrays | +| `String` | `string` | UTF-8 strings | +| `Vec` | `T[]` | Dynamic arrays | +| `[T; N]` | `T[N]` | Fixed-size arrays | + +Example: + +```rust +#[public] +impl MyContract { + pub fn process( + owner: Address, + amount: U256, + data: Bytes, + flags: Vec, + ) -> Result { + // Implementation + } +} +``` + +Generates: + +```solidity +interface IMyContract { + function process( + address owner, + uint256 amount, + bytes calldata data, + bool[] calldata flags + ) external returns (string memory); +} +``` + +### Custom errors + +Define custom errors with parameters: + +```rust +use stylus_sdk::prelude::*; + +sol! { + error InsufficientBalance(address account, uint256 requested, uint256 available); + error Unauthorized(address caller); + error InvalidAmount(); +} + +#[public] +impl Token { + pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), InsufficientBalance> { + let balance = self.balances.get(msg::sender()); + if balance < amount { + return Err(InsufficientBalance { + account: msg::sender(), + requested: amount, + available: balance, + }); + } + // Transfer logic + Ok(()) + } +} +``` + +Generated interface includes errors: + +```solidity +interface IToken { + function transfer(address to, uint256 amount) external; + + error InsufficientBalance(address account, uint256 requested, uint256 available); + error Unauthorized(address caller); + error InvalidAmount(); +} +``` + +### Events + +Events are automatically included in the ABI: + +```rust +use stylus_sdk::prelude::*; + +sol! { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +#[public] +impl Token { + pub fn transfer(&mut self, to: Address, value: U256) -> bool { + // Transfer logic + evm::log(Transfer { + from: msg::sender(), + to, + value, + }); + true + } +} +``` + +Generated interface: + +```solidity +interface IToken { + function transfer(address to, uint256 value) external returns (bool); + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} +``` + +## Trait Implementation + +Export ABIs for trait implementations: + +### Define a trait + +```rust +// ierc20.rs +use stylus_sdk::prelude::*; + +#[public] +pub trait IErc20 { + fn name(&self) -> String; + fn symbol(&self) -> String; + fn decimals(&self) -> u8; + fn total_supply(&self) -> U256; + fn balance_of(&self, owner: Address) -> U256; + fn transfer(&mut self, to: Address, value: U256) -> Result; +} +``` + +### Implement the trait + +```rust +// lib.rs +use stylus_sdk::prelude::*; + +sol_storage! { + #[entrypoint] + struct MyToken { + // Storage fields + } +} + +#[public] +#[implements(IErc20)] +impl MyToken { + // Additional functions beyond the trait + pub fn mint(&mut self, to: Address, value: U256) { + // Mint logic + } +} + +#[public] +impl IErc20 for MyToken { + fn name(&self) -> String { + "My Token".to_string() + } + + fn symbol(&self) -> String { + "MTK".to_string() + } + + fn decimals(&self) -> u8 { + 18 + } + + fn total_supply(&self) -> U256 { + self.total_supply.get() + } + + fn balance_of(&self, owner: Address) -> U256 { + self.balances.get(owner) + } + + fn transfer(&mut self, to: Address, value: U256) -> Result { + // Transfer logic + Ok(true) + } +} +``` + +Generated interface with inheritance: + +```solidity +interface IMyToken is IIErc20 { + function mint(address to, uint256 value) external; +} + +interface IIErc20 { + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); + function totalSupply() external view returns (uint256); + function balanceOf(address owner) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); +} +``` + +## Constructor Signatures + +Export constructor signatures for deployment: + +```rust +sol_storage! { + #[entrypoint] + struct MyContract { + address owner; + uint256 initial_value; + } +} + +#[public] +impl MyContract { + #[constructor] + pub fn new(owner: Address, initial_value: U256) { + self.owner.set(owner); + self.initial_value.set(initial_value); + } + + // Other methods... +} +``` + +Export constructor signature: + +```shell +cargo stylus export-abi constructor +``` + +Output: + +``` +constructor(address owner, uint256 initial_value) +``` + +For payable constructors: + +```rust +#[public] +impl MyContract { + #[constructor] + #[payable] + pub fn new(owner: Address) { + self.owner.set(owner); + // msg::value() is available + } +} +``` + +Output: + +``` +constructor(address owner) payable +``` + +## Export Configuration + +### Custom license + +Specify a custom SPDX license identifier: + +```shell +cargo stylus export-abi --license=GPL-3.0 +``` + +Output includes: + +```solidity +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.23; +``` + +### Custom pragma + +Specify a custom Solidity version pragma: + +```shell +cargo stylus export-abi --pragma="pragma solidity ^0.8.20;" +``` + +### Rust features + +Export ABI with specific Rust features enabled: + +```shell +cargo stylus export-abi --rust-features=feature1,feature2 +``` + +This is useful when your contract has conditional compilation: + +```rust +#[cfg(feature = "advanced")] +#[public] +impl MyContract { + pub fn advanced_function(&self) -> U256 { + // Advanced logic + } +} +``` + +## Integration with Front-End + +### Using ethers.js + +```typescript +import { ethers } from 'ethers'; +import MyContractABI from './abi.json'; + +const provider = new ethers.JsonRpcProvider('https://arb1.arbitrum.io/rpc'); +const contract = new ethers.Contract( + '0x1234567890123456789012345678901234567890', + MyContractABI, + provider, +); + +// Call view function +const value = await contract.getValue(); +console.log('Value:', value.toString()); + +// Call state-changing function (requires signer) +const signer = provider.getSigner(); +const contractWithSigner = contract.connect(signer); +const tx = await contractWithSigner.setValue(42); +await tx.wait(); +``` + +### Using viem + +```typescript +import { createPublicClient, http } from 'viem'; +import { arbitrum } from 'viem/chains'; +import MyContractABI from './abi.json'; + +const client = createPublicClient({ + chain: arbitrum, + transport: http(), +}); + +// Read contract +const value = await client.readContract({ + address: '0x1234567890123456789012345678901234567890', + abi: MyContractABI, + functionName: 'getValue', +}); + +// Write contract +const hash = await client.writeContract({ + address: '0x1234567890123456789012345678901234567890', + abi: MyContractABI, + functionName: 'setValue', + args: [42n], +}); +``` + +### Using wagmi/RainbowKit + +```typescript +import { useContractRead, useContractWrite } from 'wagmi'; +import MyContractABI from './abi.json'; + +function MyComponent() { + // Read contract + const { data: value } = useContractRead({ + address: '0x1234567890123456789012345678901234567890', + abi: MyContractABI, + functionName: 'getValue', + }); + + // Write contract + const { write } = useContractWrite({ + address: '0x1234567890123456789012345678901234567890', + abi: MyContractABI, + functionName: 'setValue', + }); + + return ( +
+

Current value: {value?.toString()}

+ +
+ ); +} +``` + +## Solidity Integration + +Use exported interfaces in Solidity contracts: + +### Import the interface + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import "./IMyContract.sol"; + +contract SolidityContract { + IMyContract public stylusContract; + + constructor(address _stylusContract) { + stylusContract = IMyContract(_stylusContract); + } + + function interactWithStylus() external { + // Read from Stylus contract + uint256 value = stylusContract.getValue(); + + // Write to Stylus contract + stylusContract.setValue(value + 1); + } +} +``` + +### Cross-language composition + +Combine Solidity and Rust contracts: + +```solidity +contract Router { + IToken public token; + IStaking public staking; + + constructor(address _token, address _staking) { + token = IToken(_token); // Rust contract + staking = IStaking(_staking); // Rust contract + } + + function stakeTokens(uint256 amount) external { + // Transfer tokens (Rust contract) + require( + token.transferFrom(msg.sender, address(this), amount), + "Transfer failed" + ); + + // Stake tokens (Rust contract) + token.approve(address(staking), amount); + staking.stake(msg.sender, amount); + } +} +``` + +## How It Works + +### The export-abi feature + +The `export-abi` feature enables ABI generation: + +```toml +# Cargo.toml +[features] +export-abi = ["stylus-sdk/export-abi"] + +[lib] +crate-type = ["lib", "cdylib"] +``` + +When enabled, the SDK generates: + +1. A `GenerateAbi` trait implementation +2. A CLI entry point for running ABI export +3. Formatting logic for Solidity interface generation + +### Main function + +Your contract needs a main function for ABI export: + +```rust +// main.rs +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] + +#[cfg(not(any(test, feature = "export-abi")))] +#[no_mangle] +pub extern "C" fn main() {} + +#[cfg(feature = "export-abi")] +fn main() { + my_contract::print_from_args(); +} +``` + +This main function: + +- Runs only when `export-abi` feature is enabled +- Executes the ABI generation logic +- Outputs the Solidity interface to stdout + +### The #[public] macro + +The `#[public]` macro generates ABI code: + +```rust +// From stylus-proc/src/macros/public/export_abi.rs +impl GenerateAbi for MyContract { + const NAME: &'static str = "MyContract"; + + fn fmt_abi(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "interface I{} {{", Self::NAME)?; + // Generate function signatures + write!(f, "\n function getValue() external view returns (uint256);")?; + writeln!(f, "}}")?; + Ok(()) + } +} +``` + +Key transformations: + +- `snake_case` → `camelCase` function names +- Rust types → Solidity types +- `&self` → `view`, `&mut self` → non-view +- `Result` → return type `T`, error `E` + +## Best Practices + +### 1. Always export ABIs for integration + +```shell +# ✅ Good: Generate and version control ABIs +cargo stylus export-abi > interfaces/IMyContract.sol +git add interfaces/IMyContract.sol +git commit -m "Update contract ABI" + +# ❌ Bad: Rely on manual interface definitions +``` + +### 2. Use semantic function names + +```rust +// ✅ Good: Clear, descriptive names +#[public] +impl Token { + pub fn get_balance(&self, account: Address) -> U256 { } + pub fn transfer_from(&mut self, from: Address, to: Address, amount: U256) { } +} + +// ❌ Bad: Unclear abbreviations +#[public] +impl Token { + pub fn bal(&self, acc: Address) -> U256 { } + pub fn xfer(&mut self, f: Address, t: Address, amt: U256) { } +} +``` + +### 3. Document complex functions + +```rust +#[public] +impl Staking { + /// Stakes tokens for a specified duration + /// + /// # Arguments + /// * `amount` - Amount of tokens to stake + /// * `duration` - Lock duration in seconds + /// + /// # Returns + /// The unique stake ID + pub fn stake(&mut self, amount: U256, duration: u64) -> U256 { + // Implementation + } +} +``` + +### 4. Export JSON for tooling + +```shell +# ✅ Good: Generate both formats +cargo stylus export-abi > IMyContract.sol +cargo stylus export-abi --json > abi.json + +# Share with front-end team +cp abi.json ../frontend/src/abis/ +``` + +### 5. Version control constructor changes + +When adding or modifying constructors, regenerate and commit: + +```shell +cargo stylus export-abi constructor > CONSTRUCTOR.txt +git add CONSTRUCTOR.txt +git commit -m "Update constructor signature" +``` + +### 6. Test ABI compatibility + +```typescript +// test/abi.test.ts +import { expect } from 'chai'; +import { ethers } from 'hardhat'; +import MyContractABI from '../abi.json'; + +describe('ABI Compatibility', () => { + it('should match deployed contract', async () => { + const contract = await ethers.getContractAt(MyContractABI, deployedAddress); + + // Verify functions exist + expect(contract.getValue).to.exist; + expect(contract.setValue).to.exist; + + // Call and verify + const value = await contract.getValue(); + expect(value).to.be.a('bigint'); + }); +}); +``` + +### 7. Keep interfaces synchronized + +Use CI/CD to verify ABI is up to date: + +```yaml +# .github/workflows/check-abi.yml +name: Check ABI + +on: [pull_request] + +jobs: + check-abi: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + - name: Generate ABI + run: cargo stylus export-abi > /tmp/abi.sol + - name: Check for changes + run: diff /tmp/abi.sol interfaces/IMyContract.sol +``` + +## Troubleshooting + +### solc not found + +**Error**: `failed to run solc: No such file or directory` + +**Solution**: Install Solidity compiler: + +```shell +# macOS +brew install solidity + +# Ubuntu/Debian +sudo add-apt-repository ppa:ethereum/ethereum +sudo apt-get update +sudo apt-get install solc + +# Or use solc-select +pip install solc-select +solc-select install 0.8.23 +solc-select use 0.8.23 +``` + +### Feature not enabled + +**Error**: `no main function` + +**Solution**: Ensure `export-abi` feature is defined and main.rs exists: + +```toml +# Cargo.toml +[features] +export-abi = ["stylus-sdk/export-abi"] +``` + +```rust +// main.rs +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] + +#[cfg(feature = "export-abi")] +fn main() { + my_contract::print_from_args(); +} +``` + +### Type not supported + +**Error**: `the trait AbiType is not implemented for MyType` + +**Solution**: Use supported types or implement `AbiType`: + +```rust +// ✅ Use supported types +pub fn process(&self, amount: U256) -> U256 { } + +// ❌ Custom types need AbiType implementation +pub fn process(&self, amount: MyCustomType) -> MyCustomType { } +``` + +For custom types, implement `AbiType`: + +```rust +use stylus_sdk::abi::AbiType; + +#[derive(Clone)] +struct MyType(U256); + +impl AbiType for MyType { + type SolType = alloy_sol_types::sol_data::Uint<256>; + + fn encode(&self) -> Vec { + self.0.encode() + } + + fn decode(data: &[u8]) -> Result { + U256::decode(data).map(MyType) + } +} +``` + +### Missing function in ABI + +**Error**: Function doesn't appear in exported ABI + +**Solutions**: + +1. Ensure function is in `#[public]` impl block: + + ```rust + #[public] + impl MyContract { + pub fn my_function(&self) -> U256 { } // ✅ Exported + } + + impl MyContract { + pub fn helper(&self) -> U256 { } // ❌ Not exported + } + ``` + +2. Check function visibility is `pub`: + ```rust + #[public] + impl MyContract { + pub fn exported(&self) -> U256 { } // ✅ Exported + fn not_exported(&self) -> U256 { } // ❌ Not exported + } + ``` + +## Advanced: Multiple Contracts + +Export ABIs for all contracts in a workspace: + +```shell +# Export specific contract +cargo stylus export-abi --contract=my-token + +# Export all contracts +for contract in token staking governance; do + cargo stylus export-abi --contract=$contract > interfaces/I${contract^}.sol +done +``` + +Or create a script: + +```shell +#!/bin/bash +# export-all-abis.sh + +contracts=("token" "staking" "governance") + +for contract in "${contracts[@]}"; do + echo "Exporting ABI for $contract..." + cargo stylus export-abi --contract=$contract > "interfaces/I${contract^}.sol" + cargo stylus export-abi --contract=$contract --json > "abis/${contract}.json" +done + +echo "✅ All ABIs exported" +``` + +## Resources + +- [Stylus SDK repository](https://github.com/OffchainLabs/stylus-sdk-rs) +- [Cargo Stylus CLI](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) +- [ERC-20 example](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/examples/erc20) +- [ABI specification](https://docs.soliditylang.org/en/latest/abi-spec.html) +- [ethers.js documentation](https://docs.ethers.org/) +- [viem documentation](https://viem.sh/) +- [wagmi documentation](https://wagmi.sh/) diff --git a/docs/stylus/guides/importing-interfaces.mdx b/docs/stylus/guides/importing-interfaces.mdx new file mode 100644 index 0000000000..1d93e808b7 --- /dev/null +++ b/docs/stylus/guides/importing-interfaces.mdx @@ -0,0 +1,636 @@ +--- +id: 'importing-interfaces' +title: 'Import and call external contract interfaces' +sidebar_label: 'Importing interfaces' +description: 'Learn how to import Solidity interfaces and call external contracts from your Stylus smart contracts' +author: 'anegg0' +sme: 'anegg0' +user_story: 'As a Rust developer building Stylus smart contracts, I want to interact with other contracts on the blockchain' +content_type: 'how-to' +target_audience: 'Developers building Stylus smart contracts that need to interact with other contracts' +sidebar_position: 8 +displayed_sidebar: buildStylusSidebar +--- + +import CustomDetails from '@site/src/components/CustomDetails'; +import { VanillaAdmonition } from '@site/src/components/VanillaAdmonition/'; + +Interfaces enable your Stylus contract to interact with other contracts on the blockchain, regardless of whether they're written in Solidity, Rust, or another language. This guide shows you how to import and use external contract interfaces in your Stylus smart contracts. + +## Why use interfaces + +Contract interfaces provide a type-safe way to communicate with other contracts on the blockchain. Common use cases include: + +- **Interacting with existing protocols**: Call methods on deployed Solidity contracts like ERC-20 tokens, oracles, or DeFi protocols +- **Composing functionality**: Build contracts that leverage other contracts' capabilities +- **Cross-language interoperability**: Stylus contracts can call Solidity contracts and vice versa +- **Upgradeability patterns**: Use interfaces to interact with proxy contracts + + + Since interfaces operate at the ABI level, they work identically whether the target contract is + written in Solidity, Rust, or any other language that compiles to EVM bytecode. + + +## Prerequisites + +Before implementing interfaces, ensure you have: + + + +Follow the instructions on [Rust Lang's installation page](https://www.rust-lang.org/tools/install) to install a complete Rust toolchain (v1.88 or newer) on your system. After installation, ensure you can access the programs `rustup`, `rustc`, and `cargo` from your preferred terminal application. + + + + + +In your terminal, run: + +```shell +cargo install --force cargo-stylus +``` + +Add WASM ([WebAssembly](https://webassembly.org/)) as a build target for the specific Rust toolchain you are using. The below example sets your default Rust toolchain to 1.88 as well as adding the WASM build target: + +```shell +rustup default 1.88 +rustup target add wasm32-unknown-unknown --toolchain 1.88 +``` + +You can verify that cargo stylus is installed by running `cargo stylus --help` in your terminal, which will return a list of helpful commands. + + + +## Declaring interfaces with `sol_interface!` + +The [`sol_interface!`](https://docs.rs/stylus-sdk/latest/stylus_sdk/prelude/macro.sol_interface.html) macro allows you to declare interfaces using Solidity syntax. It generates Rust structs that represent external contracts and provides type-safe methods for calling them. + +### Basic interface declaration + +```rust +use stylus_sdk::prelude::*; + +sol_interface! { + interface IToken { + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); + function approve(address spender, uint256 amount) external returns (bool); + } +} +``` + +This macro generates an `IToken` struct that you can use to call methods on any deployed contract that implements this interface. + +### Declaring multiple interfaces + +You can declare multiple interfaces in a single `sol_interface!` block: + +```rust +sol_interface! { + interface IPaymentService { + function makePayment(address user) payable returns (string); + function getBalance(address user) view returns (uint256); + } + + interface IOracle { + function getPrice(bytes32 feedId) external view returns (uint256); + function getLastUpdate() external view returns (uint256); + } + + interface IVault { + function deposit() external payable; + function withdraw(uint256 amount) external; + } +} +``` + + + Interface declarations use standard Solidity syntax. The SDK computes the correct 4-byte function + selectors based on the exact names and parameter types you provide. + + +## Calling external contract methods + +Once you've declared an interface, you can call methods on external contracts using instances of the generated struct. + +### Creating interface instances + +Use the `::new(address)` constructor to create an interface instance pointing to a deployed contract: + +```rust +use alloy_primitives::Address; + +// Create an instance pointing to a deployed token contract +let token_address = Address::from([0x12; 20]); // Replace with actual address +let token = IToken::new(token_address); +``` + +### CamelCase to snake_case conversion + +The `sol_interface!` macro converts Solidity's CamelCase method names to Rust's snake_case convention: + +| Solidity method | Rust method | +| --------------- | --------------- | +| `balanceOf` | `balance_of` | +| `makePayment` | `make_payment` | +| `getPrice` | `get_price` | +| `transferFrom` | `transfer_from` | + +The macro preserves the original CamelCase name for computing the correct function selector, so your calls reach the right method on the target contract. + +### Basic method calls + +Here's how to call methods on an external contract: + +```rust +use stylus_sdk::{call::Call, prelude::*}; +use alloy_primitives::{Address, U256}; + +sol_interface! { + interface IToken { + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); + } +} + +#[public] +impl MyContract { + pub fn check_balance(&self, token_address: Address, account: Address) -> U256 { + let token = IToken::new(token_address); + let config = Call::new(); + + token.balance_of(self.vm(), config, account).unwrap() + } +} +``` + +## Configuring your calls + +The Stylus SDK provides three `Call` constructors for different types of external calls. Choosing the correct one is essential for your contract to work properly. + +Use this decision tree to choose the correct Call constructor: + +![Call Decision Tree](/img/stylus-call-decision-tree.png) + +_Figure 2: Decision tree for selecting the appropriate Call constructor based on state modification and payment requirements._ + +### View calls with `Call::new()` + +Use `Call::new()` for read-only calls that don't modify state: + +```rust +use stylus_sdk::call::Call; + +#[public] +impl MyContract { + pub fn get_token_balance(&self, token: Address, account: Address) -> U256 { + let token_contract = IToken::new(token); + let config = Call::new(); + + token_contract.balance_of(self.vm(), config, account).unwrap() + } + + pub fn get_oracle_price(&self, oracle: Address, feed_id: [u8; 32]) -> U256 { + let oracle_contract = IOracle::new(oracle); + let config = Call::new(); + + oracle_contract.get_price(self.vm(), config, feed_id.into()).unwrap() + } +} +``` + +### State-changing calls with `Call::new_mutating(self)` + +Use `Call::new_mutating(self)` for calls that modify state on the target contract: + +```rust +#[public] +impl MyContract { + pub fn transfer_tokens( + &mut self, + token: Address, + to: Address, + amount: U256, + ) -> bool { + let token_contract = IToken::new(token); + let config = Call::new_mutating(self); + + token_contract.transfer(self.vm(), config, to, amount).unwrap() + } + + pub fn approve_spender( + &mut self, + token: Address, + spender: Address, + amount: U256, + ) -> bool { + let token_contract = IToken::new(token); + let config = Call::new_mutating(self); + + token_contract.approve(self.vm(), config, spender, amount).unwrap() + } +} +``` + + + When using `Call::new_mutating(self)`, your method must take `&mut self` as its first parameter. + This ensures the Stylus runtime properly handles state changes and reentrancy protection. + + +### Payable calls with `Call::new_payable(self, value)` + +Use `Call::new_payable(self, value)` to send ETH along with your call: + +```rust +use alloy_primitives::U256; + +sol_interface! { + interface IVault { + function deposit() external payable; + } +} + +#[public] +impl MyContract { + #[payable] + pub fn deposit_to_vault(&mut self, vault: Address) -> Result<(), Vec> { + let vault_contract = IVault::new(vault); + let value = self.vm().msg_value(); + let config = Call::new_payable(self, value); + + vault_contract.deposit(self.vm(), config)?; + Ok(()) + } + + pub fn deposit_specific_amount( + &mut self, + vault: Address, + amount: U256, + ) -> Result<(), Vec> { + let vault_contract = IVault::new(vault); + let config = Call::new_payable(self, amount); + + vault_contract.deposit(self.vm(), config)?; + Ok(()) + } +} +``` + +### Configuring gas limits + +You can limit the gas forwarded to external calls using the `.gas()` method: + +```rust +#[public] +impl MyContract { + pub fn safe_transfer( + &mut self, + token: Address, + to: Address, + amount: U256, + ) -> bool { + let token_contract = IToken::new(token); + + // Use half of remaining gas + let gas_limit = self.vm().evm_gas_left() / 2; + let config = Call::new_mutating(self).gas(gas_limit); + + token_contract.transfer(self.vm(), config, to, amount).unwrap() + } +} +``` + +### Call configuration summary + +| Constructor | Use case | State access | ETH transfer | +| -------------------------------- | --------------- | ------------ | ------------ | +| `Call::new()` | View/pure calls | Read-only | No | +| `Call::new_mutating(self)` | Write calls | Read/write | No | +| `Call::new_payable(self, value)` | Payable calls | Read/write | Yes | + +## Complete example + +Here's a complete contract that demonstrates all aspects of interface usage: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::{call::Call, prelude::*}; + +// Declare interfaces for external contracts +sol_interface! { + interface IToken { + function balanceOf(address account) external view returns (uint256); + function transfer(address to, uint256 amount) external returns (bool); + function approve(address spender, uint256 amount) external returns (bool); + function transferFrom(address from, address to, uint256 amount) external returns (bool); + } + + interface IOracle { + function getPrice(bytes32 feedId) external view returns (uint256); + } + + interface IVault { + function deposit() external payable; + function withdraw(uint256 amount) external; + } +} + +// Define events +sol! { + event TokensTransferred(address indexed token, address indexed to, uint256 amount); + event DepositMade(address indexed vault, uint256 amount); +} + +// Define errors +sol! { + error TransferFailed(address token, address to, uint256 amount); + error InsufficientBalance(uint256 have, uint256 want); +} + +#[derive(SolidityError)] +pub enum InterfaceError { + TransferFailed(TransferFailed), + InsufficientBalance(InsufficientBalance), +} + +// Contract storage +sol_storage! { + #[entrypoint] + pub struct InterfaceExample { + address owner; + address default_token; + address default_vault; + } +} + +#[public] +impl InterfaceExample { + #[constructor] + pub fn constructor(&mut self, token: Address, vault: Address) { + self.owner.set(self.vm().tx_origin()); + self.default_token.set(token); + self.default_vault.set(vault); + } + + // View call example + pub fn get_token_balance(&self, token: Address, account: Address) -> U256 { + let token_contract = IToken::new(token); + let config = Call::new(); + + token_contract.balance_of(self.vm(), config, account).unwrap() + } + + // View call with oracle + pub fn get_price(&self, oracle: Address, feed_id: [u8; 32]) -> U256 { + let oracle_contract = IOracle::new(oracle); + let config = Call::new(); + + oracle_contract.get_price(self.vm(), config, feed_id.into()).unwrap() + } + + // Mutating call example + pub fn transfer_tokens( + &mut self, + token: Address, + to: Address, + amount: U256, + ) -> Result { + let token_contract = IToken::new(token); + let config = Call::new_mutating(self); + + let success = token_contract + .transfer(self.vm(), config, to, amount) + .map_err(|_| InterfaceError::TransferFailed(TransferFailed { + token, + to, + amount, + }))?; + + if success { + self.vm().log(TokensTransferred { token, to, amount }); + } + + Ok(success) + } + + // Payable call example + #[payable] + pub fn deposit_to_vault(&mut self, vault: Address) -> Result<(), Vec> { + let vault_contract = IVault::new(vault); + let value = self.vm().msg_value(); + let config = Call::new_payable(self, value); + + vault_contract.deposit(self.vm(), config)?; + + self.vm().log(DepositMade { vault, amount: value }); + Ok(()) + } + + // Using gas limits + pub fn safe_withdraw(&mut self, vault: Address, amount: U256) -> Result<(), Vec> { + let vault_contract = IVault::new(vault); + + // Limit gas to prevent reentrancy issues + let gas_limit = self.vm().evm_gas_left() / 2; + let config = Call::new_mutating(self).gas(gas_limit); + + vault_contract.withdraw(self.vm(), config, amount)?; + Ok(()) + } + + // Complex multi-call example + pub fn swap_and_deposit( + &mut self, + token: Address, + vault: Address, + amount: U256, + ) -> Result<(), InterfaceError> { + let token_contract = IToken::new(token); + + // First, check balance + let balance = token_contract + .balance_of(self.vm(), Call::new(), self.vm().contract_address()) + .unwrap(); + + if balance < amount { + return Err(InterfaceError::InsufficientBalance(InsufficientBalance { + have: balance, + want: amount, + })); + } + + // Approve vault to spend tokens + let config = Call::new_mutating(self); + token_contract + .approve(self.vm(), config, vault, amount) + .map_err(|_| InterfaceError::TransferFailed(TransferFailed { + token, + to: vault, + amount, + }))?; + + Ok(()) + } +} +``` + +## Best practices + +### Validate addresses before calls + +Always verify that contract addresses are valid before making external calls: + +```rust +pub fn safe_transfer( + &mut self, + token: Address, + to: Address, + amount: U256, +) -> Result { + // Validate addresses + if token == Address::ZERO || to == Address::ZERO { + return Err(InterfaceError::InvalidAddress); + } + + let token_contract = IToken::new(token); + let config = Call::new_mutating(self); + + Ok(token_contract.transfer(self.vm(), config, to, amount).unwrap()) +} +``` + +### Handle call failures gracefully + +External calls can fail for various reasons. Always handle errors appropriately: + +```rust +pub fn try_transfer( + &mut self, + token: Address, + to: Address, + amount: U256, +) -> Result { + let token_contract = IToken::new(token); + let config = Call::new_mutating(self); + + match token_contract.transfer(self.vm(), config, to, amount) { + Ok(success) => Ok(success), + Err(_) => Err(InterfaceError::TransferFailed(TransferFailed { + token, + to, + amount, + })), + } +} +``` + +### Follow the checks-effects-interactions pattern + +When making external calls, update your contract's state before calling external contracts to prevent reentrancy attacks: + +```rust +pub fn withdraw_tokens( + &mut self, + token: Address, + amount: U256, +) -> Result<(), InterfaceError> { + let caller = self.vm().msg_sender(); + + // Checks + let balance = self.balances.get(caller); + if balance < amount { + return Err(InterfaceError::InsufficientBalance(InsufficientBalance { + have: balance, + want: amount, + })); + } + + // Effects - update state BEFORE external call + self.balances.setter(caller).set(balance - amount); + + // Interactions - external call last + let token_contract = IToken::new(token); + let config = Call::new_mutating(self); + token_contract.transfer(self.vm(), config, caller, amount) + .map_err(|_| InterfaceError::TransferFailed(TransferFailed { + token, + to: caller, + amount, + }))?; + + Ok(()) +} +``` + +### Use gas limits for untrusted contracts + +When calling untrusted contracts, limit the gas to prevent malicious behavior: + +```rust +pub fn call_untrusted( + &mut self, + target: Address, +) -> Result> { + let contract = IToken::new(target); + + // Limit gas to prevent griefing attacks + let config = Call::new().gas(100_000); + + Ok(contract.balance_of(self.vm(), config, self.vm().msg_sender()).unwrap()) +} +``` + +## Common pitfalls + +### Using the wrong call constructor + +Using `Call::new()` for state-changing calls will cause the transaction to fail: + +```rust +// Wrong - using Call::new() for a write operation +pub fn bad_transfer(&mut self, token: Address, to: Address, amount: U256) -> bool { + let token_contract = IToken::new(token); + let config = Call::new(); // This will fail! + token_contract.transfer(self.vm(), config, to, amount).unwrap() +} + +// Correct - using Call::new_mutating(self) +pub fn good_transfer(&mut self, token: Address, to: Address, amount: U256) -> bool { + let token_contract = IToken::new(token); + let config = Call::new_mutating(self); + token_contract.transfer(self.vm(), config, to, amount).unwrap() +} +``` + +### Forgetting to pass the VM context + +All interface method calls require `self.vm()` as the first argument: + +```rust +// Wrong - missing self.vm() +let balance = token_contract.balance_of(config, account).unwrap(); + +// Correct +let balance = token_contract.balance_of(self.vm(), config, account).unwrap(); +``` + +### Incorrect method naming + +Remember that Solidity method names are converted to snake_case in Rust: + +```rust +// Wrong - using Solidity naming +let balance = token.balanceOf(self.vm(), config, account); + +// Correct - using Rust snake_case +let balance = token.balance_of(self.vm(), config, account); +``` + +## See also + +- [Stylus contracts reference](../fundamentals/contracts.mdx#external-contract-calls): Detailed reference for external contract calls +- [Stylus by Example: Import interfaces](https://stylus-by-example.org/basic_examples/import_interfaces): Interactive examples +- [Stylus SDK documentation](https://docs.rs/stylus-sdk/latest/stylus_sdk/): Complete API reference diff --git a/docs/stylus/how-tos/optimizing-binaries.mdx b/docs/stylus/guides/optimizing-binaries.mdx similarity index 87% rename from docs/stylus/how-tos/optimizing-binaries.mdx rename to docs/stylus/guides/optimizing-binaries.mdx index d88274f8ad..7524a28812 100644 --- a/docs/stylus/how-tos/optimizing-binaries.mdx +++ b/docs/stylus/guides/optimizing-binaries.mdx @@ -6,12 +6,12 @@ sme: rauljordan target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- To be deployed onchain, the size of your **uncompressed WebAssembly (WASM) file** must not exceed 128Kb, while the **compressed binary** must not exceed 24KB. Stylus conforms with the same contract size limit as the EVM to remain fully interoperable with all smart contracts on Arbitrum chains. -[cargo-stylus](https://github.com/OffchainLabs/cargo-stylus), the Stylus CLI tool, automatically compresses your WASM programs, but there are additional steps that you can take to further reduce the size of your binaries. +[cargo-stylus](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus), the Stylus CLI tool, automatically compresses your WASM programs, but there are additional steps that you can take to further reduce the size of your binaries. Your options fall into two categories: Rust compiler flags, and third-party optimization tools. @@ -46,7 +46,7 @@ Additional WASM-specific tooling exists to shrink binaries. Due to being third p [twiggy](https://github.com/rustwasm/twiggy) is a code size profiler for WASM, it can help you estimate the impact of each added component on your binaries' size. -Our team has also curated a [list of recommended libraries](/stylus/recommended-libraries) that are helpful to Stylus development and optimally sized. +Our team has also curated a [list of recommended libraries](/stylus/advanced/recommended-libraries) that are helpful to Stylus development and optimally sized. ### Frequently asked questions diff --git a/docs/stylus/guides/using-constructors.mdx b/docs/stylus/guides/using-constructors.mdx new file mode 100644 index 0000000000..8f58324d2c --- /dev/null +++ b/docs/stylus/guides/using-constructors.mdx @@ -0,0 +1,543 @@ +--- +title: 'Using constructors with Stylus' +description: 'A comprehensive guide to implementing and deploying smart contracts with constructors in Stylus' +sidebar_label: 'Using constructors' +author: 'anegg0' +sme: 'anegg0' +user_story: 'As a Rust developer, I want to understand how to implement and use constructors in Stylus smart contracts' +content_type: 'how-to' +sidebar_position: 3 +displayed_sidebar: buildStylusSidebar +--- + +import CustomDetails from '@site/src/components/CustomDetails'; +import { VanillaAdmonition } from '@site/src/components/VanillaAdmonition/'; + +Constructors allow you to initialize your Stylus smart contracts with specific parameters when deploying them. This guide will show you how to implement constructors in Rust, understand their behavior, and deploy contracts using them. + +## What you'll accomplish + +By the end of this guide, you'll be able to: + +- Implement constructor functions in Stylus contracts +- Understand the constructor rules and limitations +- Deploy contracts with constructor parameters +- Test the constructor functionality +- Handle constructor errors and validation + +## Prerequisites + +Before implementing constructors, ensure you have: + + + +Follow the instructions on [Rust Lang's installation page](https://www.rust-lang.org/tools/install) to install a complete Rust toolchain (v1.88 or newer) on your system. After installation, ensure you can access the programs `rustup`, `rustc`, and `cargo` from your preferred terminal application. + + + + + +In your terminal, run: + +```shell +cargo install --force cargo-stylus +``` + +Add WASM ([WebAssembly](https://webassembly.org/)) as a build target for the specific Rust toolchain you are using. The below example sets your default Rust toolchain to 1.88 as well as adding the WASM build target: + +```shell +rustup default 1.88 +rustup target add wasm32-unknown-unknown --toolchain 1.88 +``` + +You can verify that cargo stylus is installed by running `cargo stylus --help` in your terminal, which will return a list of helpful commands. + + + + + +Instructions on how to set up a local Arbitrum test node can be found [in the Nitro-devnode repository](https://github.com/OffchainLabs/nitro-devnode). + + + +## Understanding Stylus constructors + +Stylus constructors provide an atomic way to deploy, activate, and initialize a contract in a single transaction. If your contract lacks a constructor, it may allow access to the contract's storage before the initialization logic runs, leading to unexpected behavior. + + + Stylus uses trait-based composition instead of traditional inheritance. When building contracts + that compose multiple traits, constructors help initialize all components properly. See the + [Constructor with trait-based composition](#constructor-with-trait-based-composition) section for + examples. + + +### Constructor rules and guarantees + +Stylus constructors follow these important rules: + +| Rule | Why it exists | +| ------------------------------------------- | --------------------------------------------------------------------------------------- | +| **Exactly 0 or 1 constructor** per contract | Mimics Solidity behavior and avoids ambiguity | +| **Must be annotated with `#[constructor]`** | Guarantees the deployer calls the correct initialization method | +| **Must take `&mut self`** | Allows writing to contract storage during deployment | +| **Returns `()` or `Result<(), Error>`** | Enables error handling; reverting aborts deployment | +| **Use `tx_origin()` for deployer address** | Factory contracts are used in deployment, so `msg_sender()` returns the factory address | +| **Constructor runs exactly once** | The SDK uses a sentinel system to prevent re-execution | + + + Stylus uses a factory pattern for deployment, which means `msg_sender()` in a constructor returns + the factory contract address, not the deployer. Always use `tx_origin()` to get the actual + deployer address. + + +## Basic constructor implementation + +Here's a simple example of a constructor in a Stylus contract: + +```rust +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] + +extern crate alloc; + +use alloy_primitives::{Address, U256}; +use alloy_sol_types::sol; +use stylus_sdk::prelude::*; + +sol! { + #[derive(Debug)] + error InvalidAmount(); +} + +sol_storage! { + #[entrypoint] + pub struct SimpleToken { + address owner; + uint256 total_supply; + string name; + string symbol; + mapping(address => uint256) balances; + } +} + +#[derive(SolidityError, Debug)] +pub enum SimpleTokenError { + InvalidAmount(InvalidAmount), +} + +#[public] +impl SimpleToken { + /// Constructor initializes the token with a name, symbol, and initial supply + #[constructor] + #[payable] + pub fn constructor( + &mut self, + name: String, + symbol: String, + initial_supply: U256, + ) -> Result<(), SimpleTokenError> { + // Validate input parameters + if initial_supply == U256::ZERO { + return Err(SimpleTokenError::InvalidAmount(InvalidAmount {})); + } + + // Get the deployer address using tx_origin() + let deployer = self.vm().tx_origin(); + + // Initialize contract state + self.owner.set(deployer); + self.name.set_str(&name); + self.symbol.set_str(&symbol); + self.total_supply.set(initial_supply); + + // Mint initial supply to deployer + self.balances.setter(deployer).set(initial_supply); + + Ok(()) + } + + // Additional contract methods... + pub fn balance_of(&self, account: Address) -> U256 { + self.balances.get(account) + } + + pub fn total_supply(&self) -> U256 { + self.total_supply.get() + } +} +``` + +### Key implementation details + +1. **Parameter validation**: Always validate constructor parameters before proceeding with initialization +2. **Error handling**: Use `Result<(), Error>` to handle initialization failures gracefully +3. **Payable constructors**: Add `#[payable]` to receive ETH during deployment +4. **State initialization**: Set all necessary contract state in the constructor + +## Advanced constructor patterns + +### Constructor with complex validation + +```rust +#[constructor] +#[payable] +pub fn constructor( + &mut self, + name: String, + symbol: String, + initial_supply: U256, + max_supply: U256, +) -> Result<(), TokenContractError> { + // Multiple validation checks + if initial_supply == U256::ZERO { + return Err(TokenContractError::InvalidAmount(InvalidAmount {})); + } + + if initial_supply > max_supply { + return Err(TokenContractError::TooManyTokens(TooManyTokens {})); + } + + if name.is_empty() || symbol.is_empty() { + return Err(TokenContractError::InvalidAmount(InvalidAmount {})); + } + + let deployer = self.vm().tx_origin(); + + // Initialize with timestamp tracking + self.owner.set(deployer); + self.name.set_str(&name); + self.symbol.set_str(&symbol); + self.total_supply.set(initial_supply); + self.max_supply.set(max_supply); + self.created_at.set(U256::from(self.vm().block_timestamp())); + + // Mint tokens to deployer + self.balances.setter(deployer).set(initial_supply); + + // Emit initialization event + log(self.vm(), TokenCreated { + creator: deployer, + name: name.clone(), + symbol: symbol.clone(), + initial_supply, + }); + + Ok(()) +} +``` + +### Constructor with trait-based composition + +Stylus uses trait-based composition instead of traditional inheritance. When implementing constructors with traits, each component typically has its own initialization logic: + +```rust +// Define traits for different functionality +trait IErc20 { + fn balance_of(&self, account: Address) -> U256; + fn transfer(&mut self, to: Address, value: U256) -> bool; +} + +trait IOwnable { + fn owner(&self) -> Address; + fn transfer_ownership(&mut self, new_owner: Address) -> bool; +} + +// Define storage for each component +#[storage] +struct Erc20Component { + balances: StorageMap, + total_supply: StorageU256, +} + +#[storage] +struct OwnableComponent { + owner: StorageAddress, +} + +// Main contract that composes functionality +#[storage] +#[entrypoint] +struct MyToken { + erc20: Erc20Component, + ownable: OwnableComponent, + name: StorageString, + symbol: StorageString, +} + +#[public] +#[implements(IErc20, IOwnable)] +impl MyToken { + #[constructor] + pub fn constructor( + &mut self, + name: String, + symbol: String, + initial_supply: U256, + ) -> Result<(), TokenError> { + // Initialize each component + self.initialize_ownable()?; + self.initialize_erc20(initial_supply)?; + + // Initialize contract-specific state + self.name.set_str(&name); + self.symbol.set_str(&symbol); + + Ok(()) + } + + fn initialize_ownable(&mut self) -> Result<(), TokenError> { + let deployer = self.vm().tx_origin(); + self.ownable.owner.set(deployer); + Ok(()) + } + + fn initialize_erc20(&mut self, initial_supply: U256) -> Result<(), TokenError> { + if initial_supply == U256::ZERO { + return Err(TokenError::InvalidSupply); + } + + let deployer = self.vm().tx_origin(); + self.erc20.total_supply.set(initial_supply); + self.erc20.balances.setter(deployer).set(initial_supply); + Ok(()) + } +} +``` + + + Unlike Solidity's inheritance, Stylus uses Rust's trait system for composition. Each component is + initialized explicitly in the constructor. + + +## Testing constructors + +The Stylus SDK provides comprehensive testing tools for constructor functionality: + +```rust +#[cfg(test)] +mod tests { + use super::*; + use stylus_sdk::testing::*; + + #[test] + fn test_constructor_success() { + let vm = TestVMBuilder::new() + .sender(Address::from([0x01; 20])) + .build(); + + let mut contract = SimpleToken::from(&vm); + + let result = contract.constructor( + "Test Token".to_string(), + "TEST".to_string(), + U256::from(1000000), + ); + + assert!(result.is_ok()); + assert_eq!(contract.name.get_string(), "Test Token"); + assert_eq!(contract.symbol.get_string(), "TEST"); + assert_eq!(contract.total_supply.get(), U256::from(1000000)); + assert_eq!( + contract.balance_of(Address::from([0x01; 20])), + U256::from(1000000) + ); + } + + #[test] + fn test_constructor_validation() { + let vm = TestVMBuilder::new() + .sender(Address::from([0x01; 20])) + .build(); + + let mut contract = SimpleToken::from(&vm); + + // Test zero supply rejection + let result = contract.constructor( + "Test Token".to_string(), + "TEST".to_string(), + U256::ZERO, + ); + + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + SimpleTokenError::InvalidAmount(_) + )); + } +} +``` + +## Deploying contracts with constructors + +### Using cargo stylus + +Deploy your contract with constructor arguments using `cargo stylus deploy`: + +```bash +# Deploy with constructor parameters +cargo stylus deploy \ + --private-key-path ~/.arbitrum/key \ + --endpoint https://sepolia-rollup.arbitrum.io/rpc \ + --constructor-args "MyToken" "MTK" 1000000 +``` + +### Constructor argument encoding + +`cargo stylus` automatically encodes the constructor arguments. The arguments should be provided in the same order as defined in your constructor function. + +For complex types: + +- **Strings**: Provide as quoted strings +- **Numbers**: Provide as decimal or hex (0x prefix) +- **Addresses**: Provide as hex strings with 0x prefix +- **Arrays**: Use JSON array syntax + +```bash +# Example with multiple argument types +cargo stylus deploy \ + --constructor-args "TokenName" "TKN" 1000000 "0x742d35Cc6635C0532925a3b8D95B5C1b0ea3C28F" +``` + +## Best practices + +### Constructor parameter validation + +Always validate constructor parameters to prevent deployment of misconfigured contracts: + +```rust +#[constructor] +pub fn constructor(&mut self, params: ConstructorParams) -> Result<(), Error> { + // Validate all parameters before any state changes + self.validate_parameters(¶ms)?; + + // Initialize state only after validation passes + self.initialize_state(params)?; + + Ok(()) +} + +fn validate_parameters(&self, params: &ConstructorParams) -> Result<(), Error> { + if params.name.is_empty() { + return Err(Error::InvalidName); + } + // Additional validation... + Ok(()) +} +``` + +### Error handling patterns + +Use descriptive error types and provide meaningful error messages: + +```rust +sol! { + #[derive(Debug)] + error InvalidName(string reason); + #[derive(Debug)] + error InvalidSupply(uint256 provided, uint256 max_allowed); + #[derive(Debug)] + error Unauthorized(address caller); +} + +#[derive(SolidityError, Debug)] +pub enum ConstructorError { + InvalidName(InvalidName), + InvalidSupply(InvalidSupply), + Unauthorized(Unauthorized), +} +``` + +### State initialization order + +Initialize contract state in a logical order to avoid dependency issues: + +```rust +#[constructor] +pub fn constructor(&mut self, params: ConstructorParams) -> Result<(), Error> { + // 1. Validate parameters first + self.validate_parameters(¶ms)?; + + // 2. Set basic contract metadata + self.name.set_str(¶ms.name); + self.symbol.set_str(¶ms.symbol); + + // 3. Set ownership and permissions + let deployer = self.vm().tx_origin(); + self.owner.set(deployer); + + // 4. Initialize token economics + self.total_supply.set(params.initial_supply); + self.max_supply.set(params.max_supply); + + // 5. Set up initial balances + self.balances.setter(deployer).set(params.initial_supply); + + // 6. Emit events last + log(self.vm(), ContractInitialized { /* ... */ }); + + Ok(()) +} +``` + +## Common pitfalls and solutions + +### Using msg_sender() instead of tx_origin() + +**Problem**: Using `msg_sender()` in constructors returns the factory contract address, not the deployer. + +```rust +// ❌ Wrong - returns factory address +let deployer = self.vm().msg_sender(); + +// ✅ Correct - returns actual deployer +let deployer = self.vm().tx_origin(); +``` + +### Missing parameter validation + +**Problem**: Not validating constructor parameters can lead to unusable contracts. + +```rust +// ❌ Wrong - no validation +#[constructor] +pub fn constructor(&mut self, supply: U256) { + self.total_supply.set(supply); // Could be zero! +} + +// ✅ Correct - validate first +#[constructor] +pub fn constructor(&mut self, supply: U256) -> Result<(), Error> { + if supply == U256::ZERO { + return Err(Error::InvalidSupply); + } + self.total_supply.set(supply); + Ok(()) +} +``` + +### Forgetting the #[constructor] annotation + +**Problem**: Functions named "constructor" without the annotation won't be recognized. + +```rust +// ❌ Wrong - missing annotation +pub fn constructor(&mut self, value: U256) { + // This won't be called during deployment +} + +// ✅ Correct - properly annotated +#[constructor] +pub fn constructor(&mut self, value: U256) { + // This will be called during deployment +} +``` + +## Summary + +Constructors in Stylus provide a powerful way to initialize your smart contracts during deployment. Key takeaways: + +- Use `#[constructor]` annotation and `&mut self` parameter +- Always use `tx_origin()` to get the deployer address +- Validate all parameters before initializing state +- Handle errors gracefully with `Result<(), Error>` return type +- Test the constructor behavior thoroughly +- Deploy with `cargo stylus deploy --constructor-args` diff --git a/docs/stylus/how-tos/using-inheritance.mdx b/docs/stylus/guides/using-inheritance.mdx similarity index 99% rename from docs/stylus/how-tos/using-inheritance.mdx rename to docs/stylus/guides/using-inheritance.mdx index 155ff8813f..a413e427d1 100644 --- a/docs/stylus/how-tos/using-inheritance.mdx +++ b/docs/stylus/guides/using-inheritance.mdx @@ -5,7 +5,7 @@ author: anegg0 sme: mahsamoosavi content_type: how-to sidebar_position: 1 -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/how-tos/_category_.yml b/docs/stylus/how-tos/_category_.yml deleted file mode 100644 index 90914ee903..0000000000 --- a/docs/stylus/how-tos/_category_.yml +++ /dev/null @@ -1,4 +0,0 @@ -label: 'How-tos' -collapsible: true -collapsed: true -position: 4 diff --git a/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx b/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx deleted file mode 100644 index 358f20ba2c..0000000000 --- a/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: 'How to verify Stylus contracts on Arbiscan' -description: 'This page provides a step-by-step guide to verifying Stylus contracts on Arbiscan, including contract details, source code submission, and handling previously verified contracts.' -author: mahsamoosavi -sme: mahsamoosavi -target_audience: 'Developers deploying smart contracts using Stylus' -content_type: how-to -sidebar_position: 1 -displayed_sidebar: buildAppsSidebar ---- - -import ImageZoom from '@site/src/components/ImageZoom'; - -This how-to will show you how to verify deployed contracts using Arbiscan, Arbitrum's block explorer. - -Here's an example of a verified contract: the [English Auction Stylus contract](https://github.com/OffchainLabs/stylus-english-auction), which has been verified on Arbitrum Sepolia. You can view the verified contract [here](https://sepolia.arbiscan.io/address/0xe85a046fd3ea22ceeb3caef3a0d38123eecbe3ca). - -You can also see a list of all Stylus contracts verified on Arbiscan by visiting: - -- [Verified Stylus Contracts on Arbitrum One](https://arbiscan.io/contractsVerified?filter=stylus). -- [Verified Stylus Contracts on Arbitrum Sepolia](https://sepolia.arbiscan.io/contractsVerified?filter=stylus). - -Here are the steps to take to verify a contract on Arbiscan: - -## Step 1: Navigate to the verification page - -You have two options to access the contract verification page on Arbiscan: - -1. **Direct link:** Visit [Arbiscan Verify Contract](https://arbiscan.io/verifyContract) to go directly to the verification form. This option is ideal if you already have the contract address and details ready. -2. **From the contract page:** If you're viewing the contract's page on Arbiscan: - - Go to the **Contract** tab. - - Click on **Verify and Publish**. - - - -Both methods will take you to the contract verification form, where you can proceed to the next step. - -## Step 2: Enter the contract's details - -You will need to fill in the following fields on the contract verification page: - -- **Contract address**: Enter the contract address you want to verify. -- **Compiler type**: Select **Stylus** for Stylus contracts. -- **Compiler version**: Choose the `cargo stylus` version that was used to deploy the contract. -- **Open source license type**: Select the appropriate license for your contract. - - - -## Step 3: Submit source code - -After entering the contract details, you’ll need to provide the contract's source code: - -- **Manual submission**: Copy and paste the source code into the provided text box. -- **Fetch from GitHub (Recommended)**: It's recommended to use the **Fetch from Git** option, as it's easier and helps automate the process. However, note that contracts located in subdirectories of the repository cannot be verified. Ensure that the contract's code is placed directly in the repository's root for verification to succeed. - - - -## Step 4: Set EVM version - -The **EVM Version to Target** can be left as default unless specific requirements dictate otherwise. - - - -## Step 5: Verify and publish - -Click **Verify and Publish**. The verification process will take a few seconds. Refresh the contract page, and if successful, the contract will be marked as verified. - - - -## Behavior when deploying a verified contract - -When deploying another instance of a previously verified contract, if the bytecode matches, Arbiscan will automatically link the new instance to the verified source code, displaying a message like: - -> "This contract matches the deployed Bytecode of the Source Code for Contract [verified contract address]." - -However, the new contract will still appear as "Not Verified" until you explicitly verify it. diff --git a/docs/stylus/how-tos/verifying-contracts.mdx b/docs/stylus/how-tos/verifying-contracts.mdx deleted file mode 100644 index 819ec19f4b..0000000000 --- a/docs/stylus/how-tos/verifying-contracts.mdx +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: 'How to verify contracts for Stylus contracts' -sidebar_label: 'Verify contracts' -description: 'Learn how to verify stylus contracts' -author: rauljordan -sme: rauljordan -target_audience: 'Developers learning how to use Stylus' -sidebar_position: 2 -user_story: 'As a current or prospective Stylus user, I want to learn how to make sure my Stylus contracts deployment are reproducible by anyone running the same environment.' -content_type: how-to -displayed_sidebar: buildAppsSidebar ---- - -:::caution -This page will walk you through how to verify your Stylus contracts locally. Stylus contract verification is also available on [Arbiscan](https://arbiscan.io/). Please note, however, that Stylus contract verification on Arbiscan is only supported for Stylus contracts deployed using `cargo-stylus` 0.5.0 or higher. -::: - -## Background - -Stylus contracts written in Rust and deployed onchain can be verified against a local codebase by using the `cargo stylus` tool. - -## Goals - -- To ensure Stylus contract deployments are reproducible by anyone who is running the same architecture as the deployed item -- To sandbox the reproducible environment and standardize it as much as possible to prevent foot guns -- To guarantee that programs reproducibly deployed with a cargo stylus version >= 0.4.2 are verifiable - -## Opting out - -By default, `cargo stylus deploy` is reproducible as it runs in a Docker container. Users can opt-out by specifying `--no-verify` as a flag. - -## Reproducible deployments - -Required knowledge and setup: - -- System architecture of your host computer (x86 / ARM) -- The git commit of your project used for deployment -- A **Rust** stylus project, such as [OffchainLabs/stylus-hello-world](https://github.com/OffchainLabs/stylus-hello-world) which contains a `rust-toolchain.toml` file -- Your cargo stylus version (run `cargo stylus --version` to obtain this value) -- [Docker](https://www.docker.com/) installed and running on your machine - -Your project's [toolchain](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file) file must contain the Rust version you wish to use for your deployment, such as `major.minor.patch` - -``` -[toolchain] -channel = "1.79.0" -``` - -It **cannot** be `stable`, `nightly`, or `beta` by itself, as a specific version must be added. For instance, you can specify `nightly-YYYY-MM-DD` or `major.minor.patch` for your channel. This is so that deployments have a very specific version to prevent potential mismatches from being more generic. - -``` -# Replace {PRIV_KEY} with your actual private key or set it as a local variable -cargo stylus deploy --private-key={PRIV_KEY} --verbose -``` - -Upon completion, you will obtain the deployment transaction hash: - -``` -deployment tx hash: 0x1d8ae97e245e1db21dd188e5b64ad9025c1fb4e5f82a8d38bc8ae2b7a387600b -``` - -Save this transaction hash, as verifiers will need it. - -## Reproducible verification - -To verify a program, the verifier will need Docker installed and also know: - -- System architecture the deployer used (x86 / ARM). Note: ARM devices that can emulate x86, such as Mac M series via Rosetta, can verify x86 Stylus deployments -- The git commit of the project the deployer used -- Your cargo stylus version the deployer used -- The deployment transaction hash - -Navigate to the project's directory and check out the git commit that was used at deployment. Ensure your `cargo stylus --version` matches what the deployer used. - -``` -# Replace {DEPLOYMENT_TX_HASH} with the actual DEPLOYMENT_TX_HASH or set it as a local variable -cargo stylus verify --deployment-tx={DEPLOYMENT_TX_HASH} -``` - -This command will run the verification pipeline through a Docker environment, recreate the project metadata hash, and verify that the deployed program matches what the command reconstructed locally. - -## How it works - -On deployment, a `keccak256` hash is created from the contents of all Rust source files in the project, sorted by file path, along with a rust-toolchain.toml, Cargo.toml and Cargo.lock files by default. This hash is injected in as a custom section of the user WASM's code. This means all data in the source files will be used for reproducible verification of a Stylus contract, including code comments. - -This means the `codehash` onchain of the program will change due to this deployment metadata hash. - -The verification routine fetches the deployment transaction by hash via RPC, then attempts to build the local project to reconstruct the deployment init code and WASM using cargo build. It then checks that the deployment transaction data matches the created init code. - -## Important details - -**Docker image** -The docker container used for reproducibility standardizes all builds to x86, and it looks like this: - -```shell -FROM --platform=linux/amd64 rust:1.79 as builder -RUN rustup toolchain install $VERSION-x86_64-unknown-linux-gnu -RUN rustup default $VERSION-x86_64-unknown-linux-gnu -RUN rustup target add wasm32-unknown-unknown -RUN rustup target add wasm32-wasi -RUN rustup target add x86_64-unknown-linux-gnu -RUN cargo install cargo-stylus -``` - -The docker container uses the `rust:1.79` version as a base for all projects. This will install cargo tooling and rust targets, but the toolchain actually used for compilation will be specified by the project being deployed in its `rust-toolchain.toml` file. - -For instance, **a future toolchain can be used** despite the base image being 1.79, as when cargo stylus is installed, it will use that particular toolchain. Future cargo stylus updates could update this base image but may not impact the compiled WASM as the image will be using the specified toolchain. However, this is why knowing the specific cargo stylus version used for the reproducible verification from the deployer is important. - -**The build toolchain** - -All verifiable Stylus contracts in Rust _must_ have a standard [rust-toolchain.toml](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file) file which specifies the channel for their deployment. It **cannot** be `stable`, `nightly`, or `beta` by itself, as a specific version must be added. For instance, you can specify `nightly-YYYY-MM-DD` or `major.minor.patch` for your channel. This is so that deployments have a very specific version to prevent potential mismatches from being more generic. diff --git a/docs/stylus/overview.mdx b/docs/stylus/overview.mdx deleted file mode 100644 index 10b1700705..0000000000 --- a/docs/stylus/overview.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -id: stylus-overview -title: Write Stylus Contracts -sidebar_label: Write Stylus contracts -displayed_sidebar: buildAppsSidebar ---- - -import Card from '@site/src/components/Cards/Card'; - -# Write Stylus Contracts - -Let's learn how to write contracts with Stylus! - -
- - - - - - - - - - - - -
diff --git a/docs/stylus/partials/_setup-cargo-stylus.mdx b/docs/stylus/partials/_setup-cargo-stylus.mdx new file mode 100644 index 0000000000..44240df72e --- /dev/null +++ b/docs/stylus/partials/_setup-cargo-stylus.mdx @@ -0,0 +1,21 @@ +### Installing cargo-stylus + +The `cargo-stylus` CLI tool helps you create, build, and deploy Stylus contracts. + +**Install:** + +```shell +cargo install cargo-stylus +``` + +**Verify:** + +```shell +cargo stylus --version +``` + +**Update:** + +```shell +cargo install cargo-stylus --force +``` diff --git a/docs/stylus/partials/_setup-docker-nitro.mdx b/docs/stylus/partials/_setup-docker-nitro.mdx new file mode 100644 index 0000000000..964ca14fa6 --- /dev/null +++ b/docs/stylus/partials/_setup-docker-nitro.mdx @@ -0,0 +1,34 @@ +### Installing Docker for local testing + +A local Arbitrum Nitro devnode allows you to test contracts before deploying to testnet. + +**macOS:** + +Download [Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/). + +**Linux:** + +```shell +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh +``` + +**Windows:** + +Download [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) (requires WSL2). + +**Start Nitro devnode:** + +```shell +git clone https://github.com/OffchainLabs/nitro-devnode.git +cd nitro-devnode +./run-dev-node.sh +``` + +**Verify:** + +```shell +curl -X POST http://localhost:8547 \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' +``` diff --git a/docs/stylus/partials/_setup-rust-toolchain.mdx b/docs/stylus/partials/_setup-rust-toolchain.mdx new file mode 100644 index 0000000000..aa63142a64 --- /dev/null +++ b/docs/stylus/partials/_setup-rust-toolchain.mdx @@ -0,0 +1,26 @@ +### Installing Rust + +The Rust toolchain includes `rustc` (compiler), `cargo` (package manager), and `rustup` (version manager). + +**macOS/Linux:** + +```shell +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +**Windows:** + +Download and run [rustup-init.exe](https://rustup.rs/). + +**Configure for WASM:** + +```shell +rustup target add wasm32-unknown-unknown +``` + +**Verify:** + +```shell +rustc --version +cargo --version +``` diff --git a/docs/stylus/reference/partials/_stylus-faucets.mdx b/docs/stylus/partials/_stylus-faucets.mdx similarity index 100% rename from docs/stylus/reference/partials/_stylus-faucets.mdx rename to docs/stylus/partials/_stylus-faucets.mdx diff --git a/docs/stylus/partials/_stylus-public-preview-banner-partial.md b/docs/stylus/partials/_stylus-public-preview-banner-partial.md new file mode 100644 index 0000000000..729f55610d --- /dev/null +++ b/docs/stylus/partials/_stylus-public-preview-banner-partial.md @@ -0,0 +1,7 @@ +:::info ALPHA RELEASE, PUBLIC PREVIEW DOCS + +Stylus is currently tagged as a `release-candidate`. The code has been audited, and testing in production environments is underway. Caution should be taken when used in production scenarios. This documentation is currently in [public preview](/stylus/concepts/public-preview-expectations). + +To provide feedback, click the **Request an update** button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). + +::: diff --git a/docs/stylus/quickstart.mdx b/docs/stylus/quickstart.mdx index a3b28c3042..85b287caeb 100644 --- a/docs/stylus/quickstart.mdx +++ b/docs/stylus/quickstart.mdx @@ -2,18 +2,21 @@ id: quickstart title: 'Quickstart: write a smart contract in Rust using Stylus' description: 'Leads a developer from 0 to 1 writing and deploying a smart contract in Rust using Stylus' +user_story: 'As a developer, I want to quickly build and deploy my first Stylus contract' +content_type: quickstart author: chrisco512, anegg0 sme: chrisco512, anegg0 sidebar_position: 2 target_audience: Developers writing Stylus contracts in Rust using Stylus -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; - +# Stylus Quickstart: write a smart contract in Rust using Stylus -This guide will get you started with Stylus' basics. We'll cover the following steps: +This guide will get you started with Stylus' basics. We'll +cover the following steps: 1. [Setting up your development environment](./quickstart#setting-up-your-development-environment) 2. [Creating a Stylus project with cargo stylus](./quickstart#creating-a-stylus-project-with-cargo-stylus) @@ -82,7 +85,7 @@ cd nitro-devnode ## Creating a Stylus project with cargo stylus -[cargo stylus](https://github.com/OffchainLabs/cargo-stylus/blob/main/main/VALID_WASM.md) is a CLI toolkit built to facilitate the development of Stylus contracts. +[cargo stylus](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) is a CLI toolkit built to facilitate the development of Stylus contracts. It is available as a plugin to the standard cargo tool used for developing Rust programs. @@ -186,7 +189,7 @@ Location: prover/src/binary.rs:493:9, data: None ``` -The contract can fail the check for various reasons (on compile, deployment, etc...). Reading the [Invalid Stylus WASM Contracts explainer](https://github.com/OffchainLabs/cargo-stylus/blob/main/main/VALID_WASM.md) can help you understand what makes a WASM contract valid or not. +The contract can fail the check for various reasons (on compile, deployment, etc...). Reading the [Invalid Stylus WASM Contracts explainer](https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/cargo-stylus/VALID_WASM.md) can help you understand what makes a WASM contract valid or not. If your contract succeeds, you'll see something like this: @@ -401,7 +404,7 @@ The testing framework allows you to: - Mock contract-to-contract interactions - Test various scenarios without deployment costs -For more advanced testing techniques and best practices, see the [Testing contracts with Stylus guide](./how-tos/testing-contracts). +For more advanced testing techniques and best practices, see the [Testing contracts with Stylus guide](./fundamentals/testing-contracts). ## Conclusion diff --git a/docs/stylus/reference/opcode-hostio-pricing.md b/docs/stylus/reference/opcode-hostio-pricing.mdx similarity index 100% rename from docs/stylus/reference/opcode-hostio-pricing.md rename to docs/stylus/reference/opcode-hostio-pricing.mdx diff --git a/docs/stylus/reference/overview.md b/docs/stylus/reference/overview.md deleted file mode 100644 index 10443bde20..0000000000 --- a/docs/stylus/reference/overview.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: 'Stylus Rust SDK overview' -description: 'An overview of the features provided by the Stylus Rust SDK' -author: jose-franco -sme: jose-franco -sidebar_position: 1 -target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. ---- - -This section provides an in-depth overview of the features provided by the [Stylus Rust SDK](https://github.com/OffchainLabs/stylus-sdk-rs). For information about deploying Rust smart contracts, see the `cargo stylus` [CLI Tool](https://github.com/OffchainLabs/cargo-stylus). For a conceptual introduction to Stylus, see [Stylus: A Gentle Introduction](../gentle-introduction.mdx). To deploy your first Stylus smart contract using Rust, refer to the [Quickstart](../quickstart.mdx). - -The Stylus Rust SDK is built on top of [Alloy](https://www.paradigm.xyz/2023/06/alloy), a collection of crates empowering the Rust Ethereum ecosystem. Because the SDK uses the same [Rust primitives for Ethereum types](https://docs.rs/alloy-primitives/latest/alloy_primitives/), Stylus is compatible with existing Rust libraries. - -The Stylus Rust SDK has been audited in August 2024 at [commit #62bd831](https://github.com/OffchainLabs/stylus-sdk-rs/tree/62bd8318c7f3ab5be954cbc264f85bf2ba3f4b06) by Open Zeppelin which can be viewed [on our audits page](audit-reports.mdx). - -This section contains a set of pages that describe a certain aspect of the Stylus Rust SDK, like how to work with [variables](https://stylus-by-example.org/basic_examples/variables), or what ways are there to [send ether](https://stylus-by-example.org/basic_examples/sending_ether). Additionally, there's also a page that compiles a set of [advanced features](/stylus/reference/rust-sdk-guide.md) that the Stylus Rust SDK provides. - -Finally, there's also a [Stylus by example](https://stylus-by-example.org) portal available that provides most of the information included in this section, as well as many different example contracts. diff --git a/docs/stylus/reference/overview.mdx b/docs/stylus/reference/overview.mdx new file mode 100644 index 0000000000..7ff6e136f1 --- /dev/null +++ b/docs/stylus/reference/overview.mdx @@ -0,0 +1,94 @@ +--- +title: 'Stylus Rust SDK overview' +description: 'An overview of the features provided by the Stylus Rust SDK' +author: jose-franco +sme: jose-franco +sidebar_position: 1 +sidebar_label: 'SDK Overview' +user_story: 'As a developer, I want to understand the Stylus Rust SDK structure so I can navigate the documentation effectively.' +content_type: 'reference' +target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +--- + +This section provides an in-depth overview of the features provided by the [Stylus Rust SDK](https://github.com/OffchainLabs/stylus-sdk-rs). For information about deploying Rust smart contracts, see the `cargo stylus` [CLI Tool](https://github.com/OffchainLabs/cargo-stylus). For a conceptual introduction to Stylus, see [Stylus: A Gentle Introduction](../gentle-introduction.mdx). To deploy your first Stylus smart contract using Rust, refer to the [Quickstart](../quickstart.mdx). + +The Stylus Rust SDK is built on top of [Alloy](https://www.paradigm.xyz/2023/06/alloy), a collection of crates empowering the Rust Ethereum ecosystem. Because the SDK uses the same [Rust primitives for Ethereum types](https://docs.rs/alloy-primitives/latest/alloy_primitives/), Stylus is compatible with existing Rust libraries. + +The Stylus Rust SDK has been audited in August 2024 at [commit #62bd831](https://github.com/OffchainLabs/stylus-sdk-rs/tree/62bd8318c7f3ab5be954cbc264f85bf2ba3f4b06) by Open Zeppelin which can be viewed [on our audits page](../../audit-reports.mdx). + +## Documentation index + +Use this index to navigate the Stylus Rust SDK reference documentation. + +### Fundamentals + +Learn the core concepts of writing smart contracts with the Stylus Rust SDK. + +| Article | Description | +| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| [Structure of a contract](/stylus/fundamentals/project-structure) | Understand how Rust contracts are organized, including project layout, the entrypoint macro, and public methods. | +| [Global variables and functions](/stylus/fundamentals/global-variables-and-functions) | Access blockchain context through the VM interface, including message sender, block info, and cryptographic functions. | +| [Contracts](/stylus/fundamentals/contracts) | Learn about contract basics, storage definition, method declarations, and the contract lifecycle. | +| [Writing tests](/stylus/fundamentals/testing-contracts) | Write and run tests for your contracts using the built-in testing framework without deploying to a blockchain. | + +### Data types + +Understand how to work with different data types in Stylus contracts. + +| Article | Description | +| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| [Primitives](/stylus/fundamentals/data-types/primitives) | Use Rust primitive types (bool, integers, addresses) with automatic ABI encoding and Solidity type mappings. | +| [Compound types](/stylus/fundamentals/data-types/compound-types) | Work with complex data structures including arrays, vectors, tuples, and custom structs. | +| [Storage](/stylus/fundamentals/data-types/storage) | Define and manage persistent contract state using storage types and the storage macro. | +| [Conversions between types](/stylus/fundamentals/data-types/conversions-between-types) | Convert between Rust types and Solidity-compatible types for cross-contract communication. | + +### Advanced topics + +Explore advanced features and optimization techniques for Stylus development. + +| Article | Description | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| [Rust to Solidity differences](/stylus/advanced/rust-to-solidity-differences) | Understand key differences between writing smart contracts in Solidity versus Stylus Rust. | +| [Recommended packages](/stylus/advanced/recommended-libraries) | Discover third-party Rust crates that work well with Stylus for extended functionality. | +| [Minimal entrypoint contracts](/stylus/advanced/minimal-entrypoint-contracts) | Build lightweight contracts with custom entrypoints for maximum gas efficiency. | +| [Hostio exports](/stylus/advanced/hostio-exports) | Access low-level host I/O functions for advanced contract behavior. | + +### Using the CLI + +Master the `cargo stylus` command-line tool for contract development and deployment. + +| Article | Description | +| ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| [Verify contracts](/stylus/cli-tools/verify-contracts) | Verify your deployed contracts on block explorers for transparency and trust. | +| [Exporting ABI](/stylus/guides/exporting-abi) | Generate Solidity-compatible ABI files for integration with frontend apps and tooling. | +| [Debugging with replay](/stylus/cli-tools/debugging-tx) | Debug failed transactions by replaying them locally with detailed execution traces. | +| [Optimizing WASM binary size](/stylus/guides/optimizing-binaries) | Reduce contract size for lower deployment costs and improved activation efficiency. | +| [Deploying non-Rust WASM contracts](/stylus/guides/deploying-non-rust-wasm-contracts) | Deploy contracts written in C, C++, or other languages that compile to WebAssembly. | + +### WASM concepts + +Understand how WebAssembly works within Arbitrum Nitro and its implications for Stylus development. + +| Article | Description | +| ---------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| [WebAssembly](/stylus/concepts/webassembly) | Learn how WASM compilation, deployment, and execution work in Arbitrum Nitro. | +| [VM differences](/stylus/concepts/vm-differences) | Understand behavioral differences between Stylus WASM execution and traditional EVM. | +| [Activation](/stylus/concepts/activation) | Learn about the contract activation process required before a Stylus contract can execute. | +| [Caching strategy](/stylus/guides/caching-contracts) | Optimize contract performance by understanding and leveraging the WASM caching system. | + +### Troubleshooting + +| Article | Description | +| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| [Troubleshooting](/stylus/troubleshooting-building-stylus) | Find solutions to common issues encountered when building and deploying Stylus contracts. | + +### External references + +Additional resources for Stylus development. + +| Resource | Description | +| ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| [Stylus by Example](https://stylus-by-example.org/) | Learn Stylus through practical, annotated code examples covering common patterns. | +| [Cargo Stylus CLI GitHub](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus) | Source code and documentation for the `cargo stylus` command-line tool. | +| [Rust SDK Crate](https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html) | Official API documentation for the Stylus SDK on docs.rs. | +| [Source Code Repository](https://github.com/OffchainLabs/stylus-sdk-rs) | Browse the complete source code for the Stylus Rust SDK. | diff --git a/docs/stylus/reference/rust-sdk-guide.md b/docs/stylus/reference/rust-sdk-guide.md index 18b05e009c..48b5d16e7b 100644 --- a/docs/stylus/reference/rust-sdk-guide.md +++ b/docs/stylus/reference/rust-sdk-guide.md @@ -9,7 +9,7 @@ target_audience: Developers using the Stylus Rust SDK to write and deploy smart import StylusNoMultiInheritanceBannerPartial from '../partials/_stylus-no-multi-inheritance-banner-partial.mdx'; -This document provides information about advanced features included in the [Stylus Rust SDK](https://github.com/OffchainLabs/stylus-sdk-rs), that are not described in the previous pages. For information about deploying Rust smart contracts, see the `cargo stylus` [CLI Tool](https://github.com/OffchainLabs/cargo-stylus). For a conceptual introduction to Stylus, see [Stylus: A Gentle Introduction](../gentle-introduction.mdx). To deploy your first Stylus smart contract using Rust, refer to the [Quickstart](../quickstart.mdx). +This document provides information about advanced features included in the [Stylus Rust SDK](https://github.com/OffchainLabs/stylus-sdk-rs), that are not described in the previous pages. For information about deploying Rust smart contracts, see the `cargo stylus` [CLI Tool](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus). For a conceptual introduction to Stylus, see [Stylus: A Gentle Introduction](../gentle-introduction.mdx). To deploy your first Stylus smart contract using Rust, refer to the [Quickstart](../quickstart.mdx). :::info @@ -364,7 +364,7 @@ Both methods can be annotated with `#[payable]` to accept ETH along with the tra ## Calls -Just as with storage and functions, Stylus SDK calls are Solidity ABI equivalent. This means you never have to know the implementation details of other contracts to invoke them. You simply import the Solidity interface of the target contract, which can be auto-generated via the `cargo stylus` [CLI tool](https://github.com/OffchainLabs/cargo-stylus#exporting-solidity-abis). +Just as with storage and functions, Stylus SDK calls are Solidity ABI equivalent. This means you never have to know the implementation details of other contracts to invoke them. You simply import the Solidity interface of the target contract, which can be auto-generated via the `cargo stylus` [CLI tool](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus#exporting-solidity-abis). :::tip diff --git a/docs/stylus/reference/testnet-information.md b/docs/stylus/reference/testnet-information.md deleted file mode 100644 index 1226e32078..0000000000 --- a/docs/stylus/reference/testnet-information.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: 'Stylus testnet information' -description: A reference providing details about the Stylus testnet and faucets for obtaining testnet ETH -author: amarrazza -sme: amarrazza -target_audience: Developers building on the Stylus testnet -sidebar_position: 9 ---- - -import StylusFaucets from './partials/_stylus-faucets.mdx'; - -import ArbitrumContractAddresses from '../../partials/_reference-arbitrum-contract-addresses-partial.mdx'; - -## Arbitrum public RPC endpoints - -:::caution - -- Unlike the RPC Urls, the Sequencer endpoints only support `eth_sendRawTransaction` and `eth_sendRawTransactionConditional` calls. -- Arbitrum public RPCs do not provide Websocket support. -- Stylus testnets v1 and v2 have been spun down and are not accessible anymore. -- Visit [Quicknode's Arbitrum Sepolia faucet](https://faucet.quicknode.com/arbitrum/sepolia), [Alchemy's Arbitrum sepolia faucet](https://www.alchemy.com/faucets/arbitrum-sepolia), or [Getblock's Arbitrum Sepolia faucet](https://getblock.io/faucet/arb-sepolia) for testnet Sepolia tokens on L2. - -::: - -This section provides an overview of the available public RPC endpoints for different Arbitrum chains that have Stylus enabled, and the necessary details to interact with them. - -| Name | RPC Url(s) | Chain ID | Block explorer | Underlying chain | Tech stack | Sequencer feed URL | Sequencer endpoint⚠️ | -| -------------------------- | -------------------------------------- | -------- | --------------------------- | ---------------- | -------------- | ------------------------------------- | ------------------------------------------------ | -| Arbitrum Sepolia (Testnet) | https://sepolia-rollup.arbitrum.io/rpc | 421614 | https://sepolia.arbiscan.io | Sepolia | Nitro (Rollup) | wss://sepolia-rollup.arbitrum.io/feed | https://sepolia-rollup-sequencer.arbitrum.io/rpc | - - - - diff --git a/docs/stylus/stylus-content-map.mdx b/docs/stylus/stylus-content-map.mdx deleted file mode 100644 index 3aa5044cc7..0000000000 --- a/docs/stylus/stylus-content-map.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -id: stylus-content-map -title: Write Stylus Contracts -sidebar_label: Write Stylus contracts -displayed_sidebar: buildAppsSidebar ---- - -import Card from '@site/src/components/Cards/Card'; - -# Write Stylus Contracts - -Let's learn how to write contracts with Stylus! - -
- - - - - - - - - - - - -
diff --git a/docs/stylus/troubleshooting/_category_.yml b/docs/stylus/troubleshooting/_category_.yml new file mode 100644 index 0000000000..bcfd42bc9e --- /dev/null +++ b/docs/stylus/troubleshooting/_category_.yml @@ -0,0 +1,5 @@ +label: 'Troubleshooting' +position: 8 +link: + type: generated-index + description: Common issues, error messages, and debugging strategies. diff --git a/docs/stylus/troubleshooting/common-issues.mdx b/docs/stylus/troubleshooting/common-issues.mdx new file mode 100644 index 0000000000..443870b9c8 --- /dev/null +++ b/docs/stylus/troubleshooting/common-issues.mdx @@ -0,0 +1,589 @@ +--- +title: 'Common issues and solutions' +sidebar_label: 'Common issues' +description: 'Troubleshooting guide for frequently encountered Stylus development issues' +user_story: 'As a developer, I want to quickly resolve common errors and issues when building with Stylus' +content_type: troubleshooting +author: offchainlabs +sme: offchainlabs +sidebar_position: 1 +--- + +This guide covers frequently encountered issues in Stylus development with step-by-step solutions. + +## Installation and setup + +### cargo stylus not found + +**Problem**: Running `cargo stylus` returns "command not found." + +**Solution**: + +```shell +# Install cargo-stylus +cargo install --force cargo-stylus + +# Verify installation +cargo stylus --version + +# If still not found, check your PATH includes ~/.cargo/bin +echo $PATH | grep cargo +``` + +**Alternative**: Add cargo bin to your PATH: + +```shell +# Add to ~/.bashrc or ~/.zshrc +export PATH="$HOME/.cargo/bin:$PATH" + +# Reload shell configuration +source ~/.bashrc # or source ~/.zshrc +``` + +### WASM target not installed + +**Problem**: Build fails with "can't find crate for `std`" or "target 'wasm32-unknown-unknown' not found." + +**Solution**: + +```shell +# Add WASM target for your Rust version +rustup target add wasm32-unknown-unknown + +# If using specific toolchain +rustup target add wasm32-unknown-unknown --toolchain 1.81 +``` + +**Verify**: + +```shell +rustup target list | grep wasm32 +# Should show: wasm32-unknown-unknown (installed) +``` + +### Docker not running + +**Problem**: `cargo stylus check` fails with "Cannot connect to Docker daemon." + +**Solution**: + +1. Start Docker Desktop +2. Verify Docker is running: + +```shell +docker ps +``` + +3. If still failing, restart Docker: + +```shell +# macOS/Linux +sudo systemctl restart docker + +# Or restart Docker Desktop application +``` + +## Build errors + +### Contract exceeds size limit + +**Problem**: "Program activation failed: program too large." + +**Solution**: + +1. **Optimize build settings** in `Cargo.toml`: + +```toml +[profile.release] +opt-level = "z" # Optimize for size +lto = true # Link-time optimization +codegen-units = 1 # Better optimization +strip = true # Remove debug symbols +panic = "abort" # Smaller panic handling +``` + +2. **Use wasm-opt**: + +```shell +cargo stylus check --wasm-opt +``` + +3. **Remove unused dependencies**: + +```toml +# Remove unnecessary features +stylus-sdk = { version = "0.8", default-features = false } +``` + +4. **Check binary size**: + +```shell +ls -lh target/wasm32-unknown-unknown/release/*.wasm +``` + +See [Optimizing binaries guide](/stylus/guides/optimizing-binaries) for more strategies. + +### Linker errors + +**Problem**: Build fails with linker errors or "undefined reference" messages. + +**Solution**: + +```shell +# Clean build artifacts +cargo clean + +# Rebuild +cargo build --target wasm32-unknown-unknown --release + +# If still failing, update Rust +rustup update +``` + +### Dependency compilation failures + +**Problem**: Dependencies fail to compile for WASM target. + +**Solution**: + +1. **Check dependency compatibility**: Not all crates support WASM. Look for: + + - `no_std` support + - WASM-compatible versions + - Alternatives designed for blockchain + +2. **Disable incompatible features**: + +```toml +[dependencies] +# Disable std-only features +serde = { version = "1.0", default-features = false, features = ["derive"] } +``` + +3. **Use Stylus-compatible alternatives**: See [recommended libraries](/stylus/advanced/recommended-libraries). + +## Deployment errors + +### Insufficient funds + +**Problem**: "Insufficient funds for gas \* price + value." + +**Solution**: + +```shell +# Check your balance +cast balance --rpc-url + +# Fund your account (testnet) +# Visit faucet: https://faucet.quicknode.com/arbitrum/sepolia +``` + +### Activation failed + +**Problem**: "Program activation failed" after deployment. + +**Solution**: + +1. **Check contract size**: + +```shell +cargo stylus check +``` + +2. **Verify WASM validity**: + +```shell +# Ensure binary is valid WASM +wasm-validate target/wasm32-unknown-unknown/release/*.wasm +``` + +3. **Check gas limits**: + +```shell +# Increase gas limit for activation +cargo stylus deploy \ + --endpoint= \ + --private-key= \ + --gas-limit=10000000 +``` + +See [Activation concepts](/stylus/concepts/activation) for more details. + +### Transaction reverted + +**Problem**: Deployment transaction reverts without clear error. + +**Solution**: + +1. **Enable verbose logging**: + +```shell +RUST_LOG=debug cargo stylus deploy \ + --endpoint= \ + --private-key= +``` + +2. **Check network status**: + +```shell +# Verify RPC is responding +cast block-number --rpc-url +``` + +3. **Use replay for debugging**: + +```shell +cargo stylus replay \ + --endpoint= \ + +``` + +## Runtime errors + +### Panic in contract code + +**Problem**: Contract panics during execution. + +**Solution**: + +1. **Use `Result` instead of `panic!`**: + +```rust +// ❌ Bad: Panics +pub fn divide(&self, a: U256, b: U256) -> U256 { + if b.is_zero() { + panic!("Division by zero"); + } + a / b +} + +// ✅ Good: Returns error +pub fn divide(&self, a: U256, b: U256) -> Result> { + ensure!(!b.is_zero(), "Division by zero"); + Ok(a / b) +} +``` + +2. **Enable backtrace for debugging**: + +```shell +RUST_BACKTRACE=1 cargo test +``` + +### Storage slot conflicts + +**Problem**: Storage values overwriting each other unexpectedly. + +**Solution**: + +1. **Use explicit storage layout**: + +```rust +sol_storage! { + pub struct MyContract { + #[borrow] // Use borrow for nested structs + StorageMap users; + + StorageU256 total_supply; + } +} +``` + +2. **Avoid manual slot manipulation** unless necessary. + +3. **Check for storage collisions** in upgradeable contracts. + +### Out of gas + +**Problem**: Transactions fail with "out of gas" error. + +**Solution**: + +1. **Optimize loops**: + +```rust +// ❌ Bad: Unbounded loop +pub fn process_all(&self, items: Vec
) -> Result<(), Vec> { + for item in items { + self.process(item)?; // Could exceed gas limit + } + Ok(()) +} + +// ✅ Good: Paginated +pub fn process_batch(&self, items: Vec
, max: usize) -> Result<(), Vec> { + for item in items.iter().take(max) { + self.process(*item)?; + } + Ok(()) +} +``` + +2. **Increase gas limit** when calling: + +```shell +cast send "function()" \ + --gas-limit 1000000 \ + --rpc-url \ + --private-key +``` + +3. **Profile gas usage**: + +```shell +cargo stylus trace --endpoint +``` + +See [Gas optimization guide](/stylus/best-practices/gas-optimization) for more strategies. + +## Testing issues + +### Tests failing with storage errors + +**Problem**: Tests fail with "storage not initialized" or similar errors. + +**Solution**: + +```rust +#[cfg(test)] +mod tests { + use super::*; + use stylus_sdk::testing::*; + + #[test] + fn test_contract() { + // ✅ Always initialize TestVM + let vm = TestVM::default(); + let mut contract = MyContract::from(&vm); + + // Configure test environment + vm.set_caller(address!("0x0000000000000000000000000000000000000001")); + + // Run tests + assert_eq!(contract.get_value().unwrap(), U256::ZERO); + } +} +``` + +### Tests pass locally but fail on CI + +**Problem**: Tests work on your machine but fail in continuous integration. + +**Solution**: + +1. **Pin Rust version** in `rust-toolchain.toml`: + +```toml +[toolchain] +channel = "1.81" +components = ["rustfmt", "clippy"] +targets = ["wasm32-unknown-unknown"] +``` + +2. **Ensure WASM target in CI**: + +```yaml +# .github/workflows/test.yml +- name: Add WASM target + run: rustup target add wasm32-unknown-unknown +``` + +3. **Check for environment-specific dependencies**. + +## ABI and integration + +### ABI export fails + +**Problem**: `cargo stylus export-abi` returns empty or incorrect ABI. + +**Solution**: + +1. **Ensure methods use `#[external]` macro**: + +```rust +#[external] +impl MyContract { + // ✅ This will be exported + pub fn public_method(&self) -> U256 { + U256::from(42) + } + + // ❌ This won't be exported (no #[external]) + fn internal_method(&self) -> U256 { + U256::from(42) + } +} +``` + +2. **Check for compilation errors**: + +```shell +cargo build --target wasm32-unknown-unknown --release +cargo stylus export-abi +``` + +### Type conversion errors + +**Problem**: "Type mismatch" when calling from Solidity or TypeScript. + +**Solution**: + +1. **Use explicit type conversions**: + +```rust +use stylus_sdk::abi::AbiType; + +// ✅ Ensure types match Solidity ABI +pub fn get_balance(&self, account: Address) -> U256 { + self.balances.get(account) +} +``` + +2. **Check ABI matches expectations**: + +```shell +cargo stylus export-abi > abi.json +# Verify types in abi.json match your frontend +``` + +See [Type conversions guide](/stylus/fundamentals/data-types/conversions-between-types). + +## Network and RPC issues + +### RPC connection timeout + +**Problem**: "Connection timeout" or "Connection refused" errors. + +**Solution**: + +1. **Verify RPC URL**: + +```shell +# Test RPC connection +curl -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' +``` + +2. **Check rate limits**: Public RPCs may have rate limits. Consider: + + - Using a dedicated RPC provider + - Adding retry logic + - Implementing backoff strategies + +3. **Network-specific endpoints**: + - Arbitrum Sepolia: https://sepolia-rollup.arbitrum.io/rpc + - Arbitrum One: https://arb1.arbitrum.io/rpc + +### Nonce errors + +**Problem**: "Nonce too low" or "nonce too high" errors. + +**Solution**: + +1. **Let tooling manage nonces** (default behavior): + +```shell +# cargo stylus automatically manages nonces +cargo stylus deploy --endpoint --private-key +``` + +2. **Reset nonce manually if needed**: + +```shell +# Check current nonce +cast nonce --rpc-url + +# Send transaction with specific nonce +cast send "function()" \ + --nonce \ + --rpc-url \ + --private-key +``` + +## Debugging strategies + +### Enable verbose logging + +```shell +# Set log level +export RUST_LOG=debug + +# Run with logging +cargo stylus check +cargo stylus deploy --endpoint --private-key +``` + +### Use replay debugging + +```shell +# Replay failed transaction +cargo stylus replay \ + --endpoint \ + + +# With GDB for advanced debugging +cargo stylus replay \ + --endpoint \ + --use-gdb \ + +``` + +See [Debugging guide](/stylus/cli-tools/debugging-tx) for detailed walkthrough. + +### Trace execution + +```shell +# Trace transaction execution +cargo stylus trace --endpoint +``` + +### Check contract state + +```shell +# Read storage values +cast storage --rpc-url + +# Call view functions +cast call "getValue()(uint256)" --rpc-url +``` + +## Getting help + +If you're still stuck: + +1. **Check documentation**: Search [Stylus docs](https://docs.arbitrum.io/stylus) +2. **Search GitHub issues**: [Stylus SDK Issues](https://github.com/OffchainLabs/stylus-sdk-rs/issues) +3. **Ask in Discord**: [Arbitrum Discord](https://discord.gg/arbitrum) #stylus channel +4. **Post in forums**: [Arbitrum Developer Forum](https://forum.arbitrum.foundation/) + +### Creating a good bug report + +Include: + +- Rust version: `rustc --version` +- cargo-stylus version: `cargo stylus --version` +- Minimal reproducible example +- Full error message and stack trace +- Steps to reproduce +- Expected vs. actual behavior + +## Common error messages quick reference + +| Error Message | Common Cause | Quick Fix | +| ------------------------------------------ | ------------------------- | ------------------------------------------ | +| "target not found: wasm32-unknown-unknown" | WASM target not installed | `rustup target add wasm32-unknown-unknown` | +| "Cannot connect to Docker daemon" | Docker not running | Start Docker Desktop | +| "Program too large" | Binary exceeds size limit | Add optimization flags, use `--wasm-opt` | +| "Insufficient funds" | Not enough ETH for gas | Fund account from faucet | +| "Nonce too low" | Nonce mismatch | Let tooling manage nonces | +| "Out of gas" | Gas limit exceeded | Increase gas limit or optimize code | +| "Storage not initialized" | Missing TestVM setup | Initialize `TestVM::default()` in tests | +| "Division by zero" | Unchecked arithmetic | Use `ensure!()` or `checked_div()` | + +## Next steps + +- Review [security best practices](/stylus/best-practices/security) +- Study [gas optimization](/stylus/best-practices/gas-optimization) +- Learn [debugging techniques](/stylus/cli-tools/debugging-tx) +- Explore [testing strategies](/stylus/fundamentals/testing-contracts) diff --git a/docs/stylus/using-cli.mdx b/docs/stylus/using-cli.mdx index 9fa36931e9..6b4cf6aac4 100644 --- a/docs/stylus/using-cli.mdx +++ b/docs/stylus/using-cli.mdx @@ -6,10 +6,10 @@ author: 'anegg0' sme: 'anegg0' sidebar_position: 2 target_audience: Developers writing Stylus contracts in Rust using Stylus -displayed_sidebar: buildAppsSidebar +displayed_sidebar: buildStylusSidebar --- -This guide will get you started using [cargo stylus](https://github.com/OffchainLabs/cargo-stylus), a CLI toolkit to help developers manage, compile, deploy, and optimize their Stylus contracts efficiently. +This guide will get you started using [cargo stylus](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus), a CLI toolkit to help developers manage, compile, deploy, and optimize their Stylus contracts efficiently. This overview will help you discover and learn how to uses cargo stylus tools. @@ -117,15 +117,15 @@ Available for commands involving transactions: #### How-tos -| Topic | Description | -| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| [Learn how to optimize WASM binaries](/stylus/how-tos/optimizing-binaries.mdx) | The `cargo-stylus` tool allows you to optimize WebAssembly (WASM) binaries, ensuring that your contracts are as efficient as possible. | -| [Debug Stylus transactions](/stylus/how-tos/debugging-tx.mdx) | A guide to debugging transactions, helping you identify and fix issues. Gain insights into your Stylus contracts by debugging transactions. | -| [Verify contracts](/stylus/how-tos/verifying-contracts.mdx) | Ensure that your Stylus contracts are correctly verified. Step-by-step instructions on how to verify your contracts using `cargo-stylus`. | -| [Run a Stylus dev node](/run-arbitrum-node/03-run-local-full-chain-simulation.mdx) | Learn how to run a local Arbitrum dev node to test your Stylus contracts. | +| Topic | Description | +| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| [Learn how to optimize WASM binaries](/stylus/guides/optimizing-binaries) | The `cargo-stylus` tool allows you to optimize WebAssembly (WASM) binaries, ensuring that your contracts are as efficient as possible. | +| [Debug Stylus transactions](/stylus/cli-tools/debugging-tx) | A guide to debugging transactions, helping you identify and fix issues. Gain insights into your Stylus contracts by debugging transactions. | +| [Verify contracts](/stylus/cli-tools/verify-contracts) | Ensure that your Stylus contracts are correctly verified. Step-by-step instructions on how to verify your contracts using `cargo-stylus`. | +| [Run a Stylus dev node](/run-arbitrum-node/run-local-full-chain-simulation) | Learn how to run a local Arbitrum dev node to test your Stylus contracts. | #### Additional resources -#### [Troubleshooting](/stylus/troubleshooting-building-stylus.md): solve the most common issues. +#### [Troubleshooting](/stylus/troubleshooting/common-issues): solve the most common issues. -#### [cargo-stylus repository](https://github.com/OffchainLabs/stylus): consult cargo stylus' source code. +#### [cargo-stylus repository](https://github.com/OffchainLabs/cargo-stylus): consult cargo stylus' source code. diff --git a/package.json b/package.json index a75f26f3fb..24647e00ea 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,11 @@ "generate-precompiles-ref-tables": "tsx scripts/precompile-reference-generator.ts", "update-variable-refs": "tsx scripts/update-variable-references.ts", "prepare": "husky || true", - "start": "yarn clear && yarn sync-stylus-content && docusaurus start", + "start": "yarn clear && yarn docusaurus start", "generate-sdk-docs": "npx docusaurus generate-typedoc", - "sync-stylus-content": "node scripts/sync-stylus-content.js", "build-glossary": "tsx scripts/build-glossary.ts", "build-translation": "yarn tsx ./scripts/move-untranslated-files.ts", - "build": "yarn install-sdk-dependencies && yarn sync-stylus-content && docusaurus build", + "build": "yarn install-sdk-dependencies && docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", @@ -36,11 +35,11 @@ }, "dependencies": { "@arbitrum/sdk": "^3.0.0", - "@docusaurus/core": "^3.6.3", - "@docusaurus/preset-classic": "^3.6.3", - "@docusaurus/theme-common": "^3.6.3", - "@docusaurus/theme-live-codeblock": "^3.6.3", - "@docusaurus/theme-mermaid": "^3.6.3", + "@docusaurus/core": "^3.9.2", + "@docusaurus/preset-classic": "^3.9.2", + "@docusaurus/theme-common": "^3.9.2", + "@docusaurus/theme-live-codeblock": "^3.9.2", + "@docusaurus/theme-mermaid": "^3.9.2", "@ethersproject/address": "^5.7.0", "@floating-ui/react": "^0.27.15", "@inkeep/cxkit-docusaurus": "^0.5.91", @@ -75,7 +74,7 @@ "devDependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", - "@docusaurus/module-type-aliases": "^3.3.2", + "@docusaurus/module-type-aliases": "^3.9.2", "@offchainlabs/notion-docs-generator": "^0.1.0", "@offchainlabs/prettier-config": "0.2.1", "@tsconfig/docusaurus": "^2.0.3", diff --git a/scripts/restructure/frontmatter-audit.sh b/scripts/restructure/frontmatter-audit.sh new file mode 100755 index 0000000000..04cc34b817 --- /dev/null +++ b/scripts/restructure/frontmatter-audit.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +echo "=== Frontmatter Audit ===" +echo "" + +missing_user_story=() +missing_content_type=() + +# Check all .mdx and .md files in stylus directory (exclude partials and implementation guides) +for file in $(find docs/stylus -name "*.mdx" -o -name "*.md" | grep -v "partials" | grep -v "IMPLEMENTATION_GUIDE" | grep -v "RESTRUCTURE_README"); do + # Check for user_story + if ! grep -q "user_story:" "$file"; then + missing_user_story+=("$file") + fi + + # Check for content_type + if ! grep -q "content_type:" "$file"; then + missing_content_type+=("$file") + fi +done + +echo "Files missing user_story:" +printf '%s\n' "${missing_user_story[@]}" +echo "" +echo "Total: ${#missing_user_story[@]}" +echo "" + +echo "Files missing content_type:" +printf '%s\n' "${missing_content_type[@]}" +echo "" +echo "Total: ${#missing_content_type[@]}" +echo "" + +if [ ${#missing_user_story[@]} -eq 0 ] && [ ${#missing_content_type[@]} -eq 0 ]; then + echo "✅ All files have complete frontmatter!" + exit 0 +else + echo "❌ Some files missing frontmatter fields" + exit 1 +fi diff --git a/scripts/restructure/terminology-audit.sh b/scripts/restructure/terminology-audit.sh new file mode 100755 index 0000000000..a0be4ca798 --- /dev/null +++ b/scripts/restructure/terminology-audit.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +echo "=== Stylus Terminology Audit ===" +echo "" + +violations=0 + +# Check for "js" instead of "JavaScript" (excluding .js file extensions, ts.js, etc.) +echo "Checking for 'js' (should be 'JavaScript')..." +results=$(grep -rn '\bjs\b' docs/stylus/ --include="*.mdx" --include="*.md" 2>/dev/null | grep -v "\.js" | grep -v "ts\.js" | grep -v "js/" | grep -v "jsx" || true) +if [ ! -z "$results" ]; then + echo "$results" + violations=$((violations + 1)) +fi + +# Check for "smartcontract" instead of "smart contract" +echo "" +echo "Checking for 'smartcontract' (should be 'smart contract')..." +results=$(grep -rin 'smartcontract' docs/stylus/ --include="*.mdx" --include="*.md" 2>/dev/null || true) +if [ ! -z "$results" ]; then + echo "$results" + violations=$((violations + 1)) +fi + +# Check for "whitelist/blacklist" instead of "allowlist/denylist" +echo "" +echo "Checking for 'whitelist/blacklist' (should be 'allowlist/denylist')..." +results=$(grep -rin 'whitelist\|blacklist' docs/stylus/ --include="*.mdx" --include="*.md" 2>/dev/null || true) +if [ ! -z "$results" ]; then + echo "$results" + violations=$((violations + 1)) +fi + +# Check for "on-chain" instead of "onchain" +echo "" +echo "Checking for 'on-chain/on chain' (should be 'onchain')..." +results=$(grep -rn 'on-chain\|on chain' docs/stylus/ --include="*.mdx" --include="*.md" 2>/dev/null || true) +if [ ! -z "$results" ]; then + echo "$results" + violations=$((violations + 1)) +fi + +# Check for "ERC20" instead of "ERC-20" +echo "" +echo "Checking for 'ERC20/ERC721' (should be 'ERC-20/ERC-721')..." +results=$(grep -rn 'ERC20\|ERC721\|ERC1155' docs/stylus/ --include="*.mdx" --include="*.md" 2>/dev/null | grep -v "ERC-" || true) +if [ ! -z "$results" ]; then + echo "$results" + violations=$((violations + 1)) +fi + +# Check for "Layer-1/L1" instead of "Parent chain" +echo "" +echo "Checking for 'Layer-1/L1/layer 1' (should be 'Parent chain')..." +results=$(grep -rin 'Layer-1\|Layer 1\|\bL1\b' docs/stylus/ --include="*.mdx" --include="*.md" 2>/dev/null || true) +if [ ! -z "$results" ]; then + echo "$results" + violations=$((violations + 1)) +fi + +# Check for "Layer-2/L2" instead of "Child chain" +echo "" +echo "Checking for 'Layer-2/L2/layer 2' (should be 'Child chain')..." +results=$(grep -rin 'Layer-2\|Layer 2\|\bL2\b' docs/stylus/ --include="*.mdx" --include="*.md" 2>/dev/null || true) +if [ ! -z "$results" ]; then + echo "$results" + violations=$((violations + 1)) +fi + +echo "" +echo "=== Audit Complete ===" +echo "Total violation types found: $violations" +echo "" + +if [ $violations -eq 0 ]; then + echo "✅ No terminology violations found!" + exit 0 +else + echo "❌ Terminology violations found. Please fix manually." + exit 1 +fi diff --git a/scripts/restructure/update-imports-phase2.sh b/scripts/restructure/update-imports-phase2.sh new file mode 100755 index 0000000000..843e88fb11 --- /dev/null +++ b/scripts/restructure/update-imports-phase2.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +echo "=== Updating imports for Phase 2 file moves ===" +echo "" + +# Update imports for files moved to fundamentals/ +echo "Updating fundamentals/ imports..." +find docs/stylus -name "*.mdx" -exec sed -i '' \ + -e 's|reference/project-structure|fundamentals/project-structure|g' \ + -e 's|reference/contracts|fundamentals/contracts|g' \ + -e 's|reference/global-variables-and-functions|fundamentals/global-variables-and-functions|g' \ + -e 's|reference/data-types|fundamentals/data-types|g' \ + -e 's|how-tos/testing-contracts|fundamentals/testing-contracts|g' \ + {} \; + +# Update imports for CLI tools +echo "Updating cli-tools/ imports..." +find docs/stylus -name "*.mdx" -exec sed -i '' \ + -e 's|using-cli\.mdx|cli-tools/overview|g' \ + -e 's|/using-cli\)|/cli-tools/overview)|g' \ + -e 's|how-tos/check-and-deploy|cli-tools/check-and-deploy|g' \ + -e 's|how-tos/debugging-tx|cli-tools/debugging-tx|g' \ + -e 's|how-tos/verifying-contracts|cli-tools/verify-contracts|g' \ + {} \; + +# Update imports for guides (how-tos → guides) +echo "Updating guides/ imports..." +find docs/stylus -name "*.mdx" -exec sed -i '' \ + -e 's|how-tos/|guides/|g' \ + {} \; + +echo "" +echo "✅ All imports updated" diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js deleted file mode 100644 index 504cfd29a6..0000000000 --- a/scripts/sync-stylus-content.js +++ /dev/null @@ -1,671 +0,0 @@ -#!/usr/bin/env node - -/** - * @fileoverview Sync content from stylus-by-example submodule to docs directory - * @description Replaces the complex 897-line stylusByExampleDocsHandler.ts with a maintainable solution - * @author sync-stylus-content.js - * @version 2.0.0 (refactored for maintainability) - */ - -const fs = require('fs-extra'); -const path = require('path'); -const glob = require('glob'); - -// ============================================================================ -// CONFIGURATION - All constants and settings in one place -// ============================================================================ - -/** - * @typedef {Object} SyncConfig - * @property {Object} paths - File and directory paths - * @property {Object} content - Content processing settings - * @property {Object} allowLists - File filtering configurations - * @property {Object} output - Output generation settings - */ - -/** @type {SyncConfig} */ -const CONFIG = { - paths: { - sourceDir: path.join(__dirname, '../submodules/stylus-by-example/src/app'), - targetDir: path.join(__dirname, '../docs/stylus-by-example'), - dontEditMarker: 'DONT-EDIT-THIS-FOLDER', - sourcePattern: '**/page.mdx', - sidebarFileName: 'sidebar.js', - }, - - content: { - fileExtensions: { - source: '.mdx', - target: '.mdx', - sidebar: '.js', - }, - markers: { - contentBegin: '{/* Begin Content */}', - frontmatterDelimiter: '---', - }, - patterns: { - metadata: /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/, - relativeLink: /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, - }, - }, - - allowLists: { - basicExamples: [ - 'hello_world', - 'primitive_data_types', - 'variables', - 'constants', - 'function', - 'errors', - 'events', - 'inheritance', - 'vm_affordances', - 'sending_ether', - 'function_selector', - 'abi_encode', - 'abi_decode', - 'hashing', - 'bytes_in_bytes_out', - ], - applications: ['erc20', 'erc721', 'vending_machine', 'multi_call'], - }, - - output: { - sections: ['basic_examples', 'applications'], - indexFileName: 'index.mdx', - sidebarTemplate: { - fileHeader: '// @ts-check', - docString: 'Autogenerated sidebar configuration for Stylus by Example', - typeAnnotation: "/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */", - }, - }, -}; - -// ============================================================================ -// UTILITY FUNCTIONS - Reusable helper functions -// ============================================================================ - -/** - * Creates a standardized error with context information - * @param {string} message - Error message - * @param {string} context - Additional context (file path, operation, etc.) - * @param {Error} [originalError] - Original error object if available - * @returns {Error} Enhanced error object - */ -function createError(message, context, originalError = null) { - const errorMessage = `${message} (Context: ${context})`; - const error = new Error(errorMessage); - error.context = context; - error.originalError = originalError; - return error; -} - -/** - * Logs messages with consistent formatting and timestamps - * @param {string} level - Log level (info, warn, error, success) - * @param {string} message - Message to log - * @param {Object} [details] - Additional details to log - */ -function log(level, message, details = null) { - const timestamp = new Date().toISOString(); - const emoji = - { - info: '🔄', - warn: '⚠️', - error: '❌', - success: '✅', - clean: '🧹', - file: '📁', - write: '📝', - }[level] || 'ℹ️'; - - console.log(`${emoji} ${message}`); - - if (details) { - console.log(` Details: ${JSON.stringify(details, null, 2)}`); - } -} - -/** - * Converts snake_case string to Title Case - * @param {string} snakeCaseStr - String in snake_case format - * @returns {string} String in Title Case format - */ -function snakeCaseToTitleCase(snakeCaseStr) { - return snakeCaseStr - .split('_') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); -} - -/** - * Safely evaluates a JavaScript object literal string - * @param {string} objectStr - String representation of JavaScript object - * @returns {Object} Parsed object - * @throws {Error} If evaluation fails - */ -function safeEvalObjectLiteral(objectStr) { - try { - // Use Function constructor to safely evaluate the object literal - return new Function(`return ${objectStr}`)(); - } catch (error) { - throw createError('Failed to evaluate object literal', objectStr, error); - } -} - -/** - * Escapes single quotes in strings for YAML frontmatter - * @param {string} str - String to escape - * @returns {string} Escaped string - */ -function escapeYamlString(str) { - return str.replace(/'/g, "''"); -} - -/** - * Generates a timestamp string for file headers - * @returns {string} ISO timestamp string - */ -function generateTimestamp() { - return new Date().toISOString(); -} - -// ============================================================================ -// PATH AND FILE UTILITIES - File system operations and path manipulation -// ============================================================================ - -/** - * Extracts section name from a file path relative to source directory - * @param {string} filePath - Full file path - * @returns {string|null} Section name ('basic_examples' or 'applications') or null - */ -function extractSectionFromPath(filePath) { - const relativePath = path.relative(CONFIG.paths.sourceDir, filePath); - const segments = relativePath.split(path.sep); - - return segments.length >= 2 ? segments[0] : null; -} - -/** - * Extracts the item name (directory name) from a file path - * @param {string} filePath - Full file path to page.mdx - * @returns {string} Directory name containing the file - */ -function extractItemNameFromPath(filePath) { - return path.basename(path.dirname(filePath)); -} - -/** - * Gets the appropriate allowlist for a section - * @param {string} sectionName - Section name ('basic_examples' or 'applications') - * @returns {string[]} Array of allowed file names - */ -function getAllowListForSection(sectionName) { - const allowListMap = { - basic_examples: CONFIG.allowLists.basicExamples, - applications: CONFIG.allowLists.applications, - }; - - return allowListMap[sectionName] || []; -} - -/** - * Determines if a file should be processed based on allowlists - * @param {string} filePath - Full path to the file - * @returns {boolean} True if file should be processed - */ -function isFileAllowed(filePath) { - const sectionName = extractSectionFromPath(filePath); - if (!sectionName) return false; - - const itemName = extractItemNameFromPath(filePath); - const allowList = getAllowListForSection(sectionName); - - return allowList.includes(itemName); -} - -/** - * Generates target file path from source file path - * @param {string} sourceFile - Source file path - * @returns {Object} Object with targetPath and metadata - */ -function generateTargetPath(sourceFile) { - const relativePath = path.relative(CONFIG.paths.sourceDir, sourceFile); - const targetDir = path.dirname(relativePath); - - let targetFileName; - let finalTargetDir; - - if (targetDir === '.') { - // Root level file - targetFileName = CONFIG.output.indexFileName; - finalTargetDir = CONFIG.paths.targetDir; - } else { - // Section subdirectory file - const pathParts = targetDir.split(path.sep); - const sectionName = pathParts[0]; - const itemName = pathParts[1]; - - targetFileName = `${itemName}${CONFIG.content.fileExtensions.target}`; - finalTargetDir = path.join(CONFIG.paths.targetDir, sectionName); - } - - return { - targetPath: path.join(finalTargetDir, targetFileName), - targetDir: finalTargetDir, - fileName: targetFileName, - }; -} - -// ============================================================================ -// CONTENT TRANSFORMATION - Text processing and content manipulation -// ============================================================================ - -/** - * Converts a metadata object to YAML frontmatter format - * @param {Object} metadata - Metadata object to convert - * @returns {string} YAML frontmatter string - */ -function convertMetadataToYaml(metadata) { - const yamlLines = Object.entries(metadata).map(([key, value]) => { - if (typeof value === 'string') { - return `${key}: '${escapeYamlString(value)}'`; - } - return `${key}: ${value}`; - }); - - return yamlLines.join('\n'); -} - -/** - * Transforms Next.js metadata export to Docusaurus frontmatter - * @param {string} content - File content with metadata export - * @returns {string} Content with frontmatter instead of metadata export - */ -function convertMetadataToFrontmatter(content) { - const match = content.match(CONFIG.content.patterns.metadata); - if (!match) return content; - - try { - const metadataStr = match[1]; - const metadata = safeEvalObjectLiteral(metadataStr); - const frontmatter = convertMetadataToYaml(metadata); - - const delimiter = CONFIG.content.markers.frontmatterDelimiter; - const replacement = `${delimiter}\n${frontmatter}\n${delimiter}`; - - return content.replace(CONFIG.content.patterns.metadata, replacement); - } catch (error) { - log('warn', `Could not convert metadata: ${error.message}`); - return content; - } -} - -/** - * Fixes relative links to work with Docusaurus URL structure - * @param {string} content - Content with relative links - * @returns {string} Content with fixed absolute links - */ -function fixRelativeLinks(content) { - return content.replace(CONFIG.content.patterns.relativeLink, (match, linkText, dirName) => { - // Convert to absolute Docusaurus path - return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; - }); -} - -/** - * Adds content begin marker if not already present - * @param {string} content - File content - * @returns {string} Content with begin marker - */ -function addContentBeginMarker(content) { - const marker = CONFIG.content.markers.contentBegin; - if (content.includes(marker)) return content; - - const delimiter = CONFIG.content.markers.frontmatterDelimiter; - const frontmatterEnd = content.indexOf(delimiter, 3); - - if (frontmatterEnd === -1) return content; - - const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; - return content.slice(0, insertPos) + `\n${marker}\n` + content.slice(insertPos); -} - -/** - * Adds "not for production" banner import and component two lines before first rust code block - * @param {string} content - File content - * @returns {string} Content with banner added - */ -function addNotForProductionBanner(content) { - const firstCodeBlock = '```rust'; - const bannerCode = ` -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - -`; - - // Find the first occurrence of ```rust code block - const index = content.indexOf(firstCodeBlock); - if (index === -1) { - // No rust code block found, return content unchanged - return content; - } - - // Find the position two lines before the first code block - const lines = content.substring(0, index).split('\n'); - const insertLineIndex = lines.length - 2; - - // Insert the banner at the calculated position - lines.splice(insertLineIndex, 0, bannerCode); - - // Reconstruct the content with the banner inserted - const newContent = lines.join('\n') + content.substring(index); - return newContent; -} - -/** - * Applies all content transformations to a file - * @param {string} content - Original file content - * @returns {string} Transformed content - */ -function transformContent(content) { - let transformedContent = content; - - // Apply transformations in sequence - transformedContent = convertMetadataToFrontmatter(transformedContent); - transformedContent = fixRelativeLinks(transformedContent); - transformedContent = addContentBeginMarker(transformedContent); - transformedContent = addNotForProductionBanner(transformedContent); - - return transformedContent; -} - -// ============================================================================ -// SIDEBAR GENERATION - Sidebar configuration file generation -// ============================================================================ - -/** - * Creates a sidebar item object for a file - * @param {string} sectionName - Section name (basic_examples, applications) - * @param {string} fileName - File name without extension - * @returns {Object} Sidebar item configuration - */ -function createSidebarItem(sectionName, fileName) { - return { - type: 'doc', - id: `stylus-by-example/${sectionName}/${fileName}`, - label: snakeCaseToTitleCase(fileName), - }; -} - -/** - * Generates the complete sidebar JavaScript file content - * @param {string} sectionName - Section name for the sidebar - * @param {Object[]} sidebarItems - Array of sidebar item objects - * @returns {string} Complete sidebar file content - */ -function generateSidebarFileContent(sectionName, sidebarItems) { - const template = CONFIG.output.sidebarTemplate; - const timestamp = generateTimestamp(); - - return `${template.fileHeader} -/** - * @fileoverview ${template.docString} ${sectionName} - * @description This file is automatically generated by sync-stylus-content.js - * @generated ${timestamp} - * @see https://docusaurus.io/docs/sidebar - * @see https://docusaurus.io/docs/sidebar/items - */ - -${template.typeAnnotation} -const sidebar = ${JSON.stringify({ items: sidebarItems }, null, 2)}; - -module.exports = sidebar.items; -`; -} - -/** - * Generates sidebar configuration for a section directory - * @param {string} sectionName - Section name (basic_examples, applications) - * @param {string[]} files - Array of file paths - * @returns {string} Complete sidebar JavaScript file content - */ -function generateSidebar(sectionName, files) { - const allowList = getAllowListForSection(sectionName); - const itemsByFileName = {}; - - // Collect all items by filename, skipping index files - files.forEach((file) => { - const fileName = path.basename(file, CONFIG.content.fileExtensions.source); - - // Skip index files (filename matches section name) - if (fileName === sectionName) return; - - const item = createSidebarItem(sectionName, fileName); - itemsByFileName[fileName] = item; - }); - - // Order items according to allowlist - const orderedItems = allowList - .map((allowedFileName) => itemsByFileName[allowedFileName]) - .filter(Boolean); // Remove undefined items - - return generateSidebarFileContent(sectionName, orderedItems); -} - -// ============================================================================ -// FILE OPERATIONS - High-level file processing functions -// ============================================================================ - -/** - * Processes and copies a single MDX file with transformations - * @param {string} sourcePath - Source file path - * @param {string} targetPath - Target file path - * @returns {Promise} True if processing succeeded - */ -async function processFile(sourcePath, targetPath) { - try { - // Read and transform content - const content = await fs.readFile(sourcePath, 'utf8'); - const transformedContent = transformContent(content); - - // Ensure target directory exists and write file - await fs.ensureDir(path.dirname(targetPath)); - await fs.writeFile(targetPath, transformedContent); - - return true; - } catch (error) { - log('error', `Failed to process file: ${sourcePath}`, { - error: error.message, - targetPath, - }); - return false; - } -} - -/** - * Creates the "don't edit" marker file in target directory - * @param {string} targetDir - Target directory path - * @param {boolean} isSubmoduleAvailable - Whether submodule is available - * @returns {Promise} - */ -async function createDontEditMarker(targetDir, isSubmoduleAvailable = true) { - const timestamp = generateTimestamp(); - const markerPath = path.join(targetDir, CONFIG.paths.dontEditMarker); - - const content = isSubmoduleAvailable - ? `This folder is auto-generated from submodules/stylus-by-example -Do not edit files directly here. Edit them in the submodule instead. -Generated: ${timestamp} -` - : `This folder would contain auto-generated content from submodules/stylus-by-example -The submodule was not available during build. -Generated: ${timestamp} -`; - - await fs.writeFile(markerPath, content); -} - -/** - * Creates empty sidebar files for sections when submodule is unavailable - * @param {string} targetDir - Target directory path - * @returns {Promise} - */ -async function createEmptySidebars(targetDir) { - const timestamp = generateTimestamp(); - const template = CONFIG.output.sidebarTemplate; - - const emptySidebarContent = `${template.fileHeader} -/** - * @fileoverview Empty sidebar configuration (submodule not available) - * @generated ${timestamp} - */ - -${template.typeAnnotation} -const sidebar = { - items: [] -}; - -module.exports = sidebar.items; -`; - - for (const sectionName of CONFIG.output.sections) { - const sectionDir = path.join(targetDir, sectionName); - await fs.ensureDir(sectionDir); - - const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); - await fs.writeFile(sidebarPath, emptySidebarContent); - } -} - -/** - * Generates sidebar files for all processed sections - * @param {string} targetDir - Target directory path - * @returns {Promise} - */ -async function generateAllSidebars(targetDir) { - for (const sectionName of CONFIG.output.sections) { - const sectionDir = path.join(targetDir, sectionName); - - if (await fs.pathExists(sectionDir)) { - const pattern = path.join(sectionDir, `**/*${CONFIG.content.fileExtensions.source}`); - const sectionFiles = glob.sync(pattern); - - const sidebarContent = generateSidebar(sectionName, sectionFiles); - const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); - - await fs.writeFile(sidebarPath, sidebarContent); - log('write', `Generated sidebar for ${sectionName}`, { - itemCount: sectionFiles.length, - }); - } - } -} - -// ============================================================================ -// MAIN SYNC LOGIC - Core synchronization workflow -// ============================================================================ - -/** - * Handles the case when the source submodule is not available - * @returns {Promise} - */ -async function handleMissingSubmodule() { - log('warn', `Source directory not found: ${CONFIG.paths.sourceDir}`); - log('info', 'Skipping Stylus by Example content sync.'); - log('info', 'To include this content, initialize the submodule:'); - log('info', ' git submodule update --init --recursive'); - - // Create empty structure - await fs.ensureDir(CONFIG.paths.targetDir); - await createDontEditMarker(CONFIG.paths.targetDir, false); - await createEmptySidebars(CONFIG.paths.targetDir); - - log('success', 'Created empty Stylus by Example structure'); -} - -/** - * Processes all allowed files from source to target directory - * @param {string[]} allowedFiles - Array of file paths to process - * @returns {Promise} Number of successfully processed files - */ -async function processAllFiles(allowedFiles) { - let successCount = 0; - - for (const sourceFile of allowedFiles) { - const { targetPath } = generateTargetPath(sourceFile); - - if (await processFile(sourceFile, targetPath)) { - successCount++; - } - } - - return successCount; -} - -/** - * Main synchronization function - * @returns {Promise} - */ -async function syncContent() { - log('info', 'Syncing Stylus by Example content...'); - - try { - // Check if source exists - if (!(await fs.pathExists(CONFIG.paths.sourceDir))) { - await handleMissingSubmodule(); - return; - } - - // Clean and recreate target directory - if (await fs.pathExists(CONFIG.paths.targetDir)) { - log('clean', 'Cleaning target directory...'); - await fs.remove(CONFIG.paths.targetDir); - } - - await fs.ensureDir(CONFIG.paths.targetDir); - await createDontEditMarker(CONFIG.paths.targetDir, true); - - // Find and filter files - const sourcePattern = path.join(CONFIG.paths.sourceDir, CONFIG.paths.sourcePattern); - const allFiles = glob.sync(sourcePattern); - const allowedFiles = allFiles.filter(isFileAllowed); - - log( - 'file', - `Found ${allFiles.length} total files, ${allowedFiles.length} allowed files to process`, - ); - - // Process all files - const successCount = await processAllFiles(allowedFiles); - - // Generate sidebars - await generateAllSidebars(CONFIG.paths.targetDir); - - log('success', `Successfully synced ${successCount}/${allowedFiles.length} files`); - } catch (error) { - log('error', `Sync failed: ${error.message}`, { - stack: error.stack, - context: error.context || 'Unknown', - }); - process.exit(1); - } -} - -// ============================================================================ -// MODULE EXPORTS AND EXECUTION -// ============================================================================ - -// Run if called directly -if (require.main === module) { - syncContent(); -} - -module.exports = { - syncContent, - // Export utilities for testing - CONFIG, - isFileAllowed, - transformContent, - generateSidebar, -}; diff --git a/sidebars.js b/sidebars.js index 7a74dfc2e4..2146cf3cf1 100644 --- a/sidebars.js +++ b/sidebars.js @@ -3,8 +3,6 @@ // Use the generated SDK sidebar for API reference const sdkApiSidebar = require('./sdk-sidebar.js'); // Use the generated stylus-by-example sidebars -const stylusByExampleBasicExamples = require('./docs/stylus-by-example/basic_examples/sidebar.js'); -const stylusByExampleApplications = require('./docs/stylus-by-example/applications/sidebar.js'); // Create a custom SDK sidebar that combines manual intro pages with generated API docs const sdkSidebar = { @@ -113,10 +111,6 @@ const sidebars = { type: 'category', label: 'Oracles', collapsed: true, - link: { - type: 'doc', - id: 'for-devs/oracles/oracles-content-map', - }, items: [ { type: 'doc', @@ -824,11 +818,6 @@ const sidebars = { id: 'run-arbitrum-node/arbos-releases/overview', label: 'Overview', }, - { - type: 'doc', - id: 'run-arbitrum-node/arbos-releases/arbos51', - label: 'Dia (ArbOS 51)', - }, { type: 'doc', id: 'run-arbitrum-node/arbos-releases/arbos40', @@ -877,10 +866,6 @@ const sidebars = { type: 'category', label: 'Sequencer', collapsed: true, - link: { - type: 'doc', - id: 'node-running/sequencer-content-map', - }, items: [ { type: 'doc', @@ -1393,10 +1378,262 @@ const sidebars = { }, ], }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Solidity-focused sidebar (only shows Solidity content) + buildSoliditySidebar: [ + { + type: 'category', + label: 'Build apps with Solidity', + collapsed: false, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Quickstart', + }, + { + type: 'doc', + label: 'Estimate gas', + id: 'build-decentralized-apps/how-to-estimate-gas', + }, + { + type: 'doc', + label: 'Chains and testnets', + id: 'build-decentralized-apps/public-chains', + }, + { + type: 'doc', + label: 'Cross-chain messaging', + id: 'build-decentralized-apps/cross-chain-messaging', + }, + { + type: 'doc', + id: 'build-decentralized-apps/custom-gas-token-sdk', + label: 'Custom gas token SDK', + }, + { + type: 'category', + label: 'Arbitrum vs Ethereum', + items: [ + { + type: 'doc', + label: 'Comparison overview', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + }, + { + type: 'doc', + label: 'Block gas limit, numbers and time', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + }, + { + type: 'doc', + label: 'RPC methods', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + }, + { + type: 'doc', + label: 'Solidity support', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + }, + ], + }, + { + type: 'doc', + label: 'Oracles', + id: 'build-decentralized-apps/oracles/overview-oracles', + }, + { + type: 'category', + label: 'Precompiles', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/precompiles/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/precompiles/reference', + }, + ], + }, + { + type: 'category', + label: 'NodeInterface', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/nodeinterface/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/nodeinterface/reference', + }, + ], + }, + { + type: 'category', + label: 'Token bridging', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/token-bridging/overview', + }, + { + type: 'doc', + label: 'ETH bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + }, + { + type: 'doc', + label: 'ERC-20 token bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + }, + { + type: 'category', + label: 'Bridge tokens programmatically', + items: [ + { + type: 'doc', + label: 'Get started', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', + }, + { + type: 'doc', + label: 'Use the standard gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', + }, + { + type: 'doc', + label: 'Use the generic-custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', + }, + { + type: 'doc', + label: 'Use the custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/reference/node-providers', + label: 'RPC endpoints and providers', + }, + { + type: 'doc', + label: 'Smart contract addresses', + id: 'build-decentralized-apps/reference/contract-addresses', + }, + { + type: 'doc', + label: 'Chain parameters', + id: 'build-decentralized-apps/reference/chain-params', + }, + { + type: 'doc', + label: 'Development frameworks', + id: 'build-decentralized-apps/reference/development-frameworks', + }, + { + type: 'doc', + label: 'Web3 libraries and tools', + id: 'build-decentralized-apps/reference/web3-libraries-tools', + }, + { + type: 'doc', + label: 'Monitoring tools and block explorers', + id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + }, + { + type: 'doc', + label: 'Debugging tools', + id: 'build-decentralized-apps/reference/debugging-tools', + }, + + { + type: 'doc', + id: 'build-decentralized-apps/reference/mainnet-risks', + label: 'Mainnet risks', + }, + ], + }, + { + type: 'doc', + label: 'Troubleshooting', + id: 'for-devs/troubleshooting-building', + }, + { + type: 'category', + label: 'Arbitrum SDK', + items: sdkSidebar.sdkSidebar, + }, + { + type: 'link', + label: 'Tutorials', + href: 'https://github.com/OffchainLabs/arbitrum-tutorials', + }, + ], + }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Stylus-focused sidebar (only shows Stylus content) + buildStylusSidebar: [ { type: 'category', label: 'Build apps with Stylus', - collapsed: true, + collapsed: false, + link: { + type: 'generated-index', + title: 'Build apps with Stylus', + description: + "Learn how to build decentralized applications using Stylus, Arbitrum's next-generation smart contract platform supporting Rust, C, and C++.", + slug: '/stylus', + }, items: [ { type: 'doc', @@ -1410,106 +1647,252 @@ const sidebars = { }, { type: 'category', - label: 'Rust SDK', - collapsed: true, + label: 'Fundamentals', + collapsed: false, + link: { + type: 'generated-index', + }, items: [ { type: 'doc', - id: 'stylus/reference/overview', - label: 'Overview', + id: 'stylus/fundamentals/choose-your-path', + label: 'Choose your path', }, { type: 'doc', - id: 'stylus/reference/project-structure', - label: 'Structure of a Contract', + id: 'stylus/fundamentals/prerequisites', + label: 'Prerequisites', }, - ...stylusByExampleBasicExamples, { type: 'doc', - id: 'stylus/how-tos/using-inheritance', - label: 'Composition and trait-based routing model', + id: 'stylus/fundamentals/project-structure', + label: 'Project structure', }, { type: 'doc', - id: 'stylus/reference/rust-sdk-guide', - label: 'Advanced features', + id: 'stylus/fundamentals/contracts', + label: 'Contract structure', }, { type: 'doc', - id: 'stylus/recommended-libraries', - label: 'Recommended Rust Crates', + id: 'stylus/fundamentals/global-variables-and-functions', + label: 'Global variables and functions', + }, + { + type: 'category', + label: 'Data types', + items: [ + { + type: 'doc', + id: 'stylus/fundamentals/data-types/primitives', + label: 'Primitives', + }, + { + type: 'doc', + id: 'stylus/fundamentals/data-types/compound-types', + label: 'Compound types', + }, + { + type: 'doc', + id: 'stylus/fundamentals/data-types/storage', + label: 'Storage', + }, + { + type: 'doc', + id: 'stylus/fundamentals/data-types/conversions-between-types', + label: 'Conversions between types', + }, + ], + }, + { + type: 'doc', + id: 'stylus/fundamentals/testing-contracts', + label: 'Testing contracts', }, ], }, { type: 'category', - label: 'Rust CLI', - collapsed: true, + label: 'Guides', items: [ { type: 'doc', - id: 'stylus/using-cli', - label: 'Overview', + id: 'stylus/guides/using-constructors', + label: 'Using constructors', }, { type: 'doc', - id: 'stylus/how-tos/debugging-tx', - label: 'Debug transactions', + id: 'stylus/guides/using-inheritance', + label: 'Using inheritance', }, { type: 'doc', - id: 'stylus/how-tos/testing-contracts', - label: 'Testing contracts', + id: 'stylus/guides/importing-interfaces', + label: 'Importing interfaces', }, { type: 'doc', - id: 'stylus/how-tos/verifying-contracts', - label: 'Verify contracts', + id: 'stylus/guides/exporting-abi', + label: 'Exporting ABI', + }, + { + type: 'doc', + id: 'stylus/guides/optimizing-binaries', + label: 'Optimizing binaries', }, { type: 'doc', - id: 'stylus/how-tos/caching-contracts', - label: 'Cache contracts', + id: 'stylus/guides/caching-contracts', + label: 'Caching contracts', }, { type: 'doc', - id: 'stylus/how-tos/verifying-contracts-arbiscan', - label: 'Verify on Arbiscan', + id: 'stylus/guides/deploying-non-rust-wasm-contracts', + label: 'Deploy non-Rust contracts', }, { type: 'doc', - id: 'stylus/how-tos/optimizing-binaries', - label: 'Optimize WASM binaries', + id: 'stylus/guides/adding-support-for-new-languages', + label: 'Add language support', }, ], }, { - type: 'html', - value: - 'Run a local dev node', + type: 'category', + label: 'CLI tools', + items: [ + { + type: 'doc', + id: 'stylus/cli-tools/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/cli-tools/check-and-deploy', + label: 'Check and deploy', + }, + { + type: 'doc', + id: 'stylus/cli-tools/verify-contracts', + label: 'Verify contracts', + }, + { + type: 'doc', + id: 'stylus/cli-tools/debugging-tx', + label: 'Debugging transactions', + }, + { + type: 'doc', + id: 'stylus/cli-tools/commands-reference', + label: 'Commands reference', + }, + ], }, { type: 'category', - label: 'Examples', + label: 'Best Practices', collapsed: true, + link: { + type: 'generated-index', + }, items: [ - ...stylusByExampleApplications, { - type: 'link', - label: 'Awesome Stylus', - href: 'https://github.com/OffchainLabs/awesome-stylus', + type: 'doc', + id: 'stylus/best-practices/security', + label: 'Security', + }, + { + type: 'doc', + id: 'stylus/best-practices/gas-optimization', + label: 'Gas optimization', }, ], }, { type: 'category', - label: 'Reference', + label: 'Troubleshooting', collapsed: true, + link: { + type: 'generated-index', + }, + items: [ + { + type: 'doc', + id: 'stylus/troubleshooting/common-issues', + label: 'Common issues', + }, + ], + }, + { + type: 'category', + label: 'Concepts', + items: [ + { + type: 'doc', + id: 'stylus/concepts/webassembly', + label: 'WebAssembly', + }, + { + type: 'doc', + id: 'stylus/concepts/activation', + label: 'Activation', + }, + { + type: 'doc', + id: 'stylus/concepts/gas-metering', + label: 'Gas metering', + }, + { + type: 'doc', + id: 'stylus/concepts/vm-differences', + label: 'VM differences', + }, + { + type: 'doc', + id: 'stylus/concepts/public-preview-expectations', + label: 'Public preview expectations', + }, + ], + }, + { + type: 'category', + label: 'Advanced', items: [ + { + type: 'doc', + id: 'stylus/advanced/rust-to-solidity-differences', + label: 'Rust to Solidity', + }, + { + type: 'doc', + id: 'stylus/advanced/minimal-entrypoint-contracts', + label: 'Minimal entrypoint contracts', + }, + { + type: 'doc', + id: 'stylus/advanced/hostio-exports', + label: 'Hostio exports', + }, + { + type: 'doc', + id: 'stylus/advanced/recommended-libraries', + label: 'Recommended libraries', + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'stylus/reference/overview', + label: 'Overview', + }, { type: 'doc', id: 'stylus/reference/opcode-hostio-pricing', - label: 'Gas & Ink Pricing', + label: 'Opcode and hostio pricing', }, { type: 'link', @@ -1519,7 +1902,7 @@ const sidebars = { { type: 'link', label: 'Cargo Stylus CLI GitHub', - href: 'https://github.com/OffchainLabs/cargo-stylus', + href: 'https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus', }, { type: 'link', @@ -1529,24 +1912,14 @@ const sidebars = { { type: 'link', label: 'Source Code Repository', - href: 'https://github.com/OffchainLabs/stylus', + href: 'https://github.com/OffchainLabs/stylus-sdk-rs', }, ], }, - { - type: 'doc', - id: 'stylus/concepts/gas-metering', - label: 'Gas metering', - }, - { - type: 'doc', - id: 'stylus/how-tos/adding-support-for-new-languages', - label: 'Using other languages', - }, { type: 'doc', id: 'stylus/troubleshooting-building-stylus', - label: 'Troubleshooting', + label: 'FAQ', }, ], }, diff --git a/sidebars.js.backup b/sidebars.js.backup new file mode 100644 index 0000000000..e467c4fd91 --- /dev/null +++ b/sidebars.js.backup @@ -0,0 +1,1809 @@ +// @ts-check + +// Use the generated SDK sidebar for API reference +const sdkApiSidebar = require('./sdk-sidebar.js'); +// Use the generated stylus-by-example sidebars + +// Create a custom SDK sidebar that combines manual intro pages with generated API docs +const sdkSidebar = { + sdkSidebar: [ + { + type: 'doc', + id: 'sdk/index', + label: 'Introduction', + }, + { + type: 'doc', + id: 'sdk/migrate', + label: 'Migrate from v3 to v4', + }, + ...sdkApiSidebar.sdkSidebar, + ], +}; + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // Get started sidebar - shared entry point + getStartedSidebar: [ + { + type: 'doc', + id: 'get-started/overview', + label: 'Get started', + }, + { + type: 'doc', + id: 'get-started/arbitrum-introduction', + label: 'Arbitrum: introduction', + }, + { + type: 'category', + label: 'Build apps', + collapsed: true, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Build apps with Solidity', + }, + { + type: 'doc', + id: 'stylus/quickstart', + label: 'Build apps with Stylus', + }, + ], + }, + { + type: 'link', + label: 'Run an Arbitrum (Orbit) chain', + href: '/launch-arbitrum-chain/a-gentle-introduction', + }, + { + type: 'link', + label: 'Run an Arbitrum node', + href: '/run-arbitrum-node/overview', + }, + { + type: 'link', + label: 'Arbitrum bridge', + href: '/arbitrum-bridge/quickstart', + }, + { + type: 'link', + label: 'How Arbitrum works', + href: '/how-arbitrum-works/inside-arbitrum-nitro', + }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, + { + type: 'doc', + id: 'intro/glossary', + label: 'Glossary', + }, + { + type: 'doc', + id: 'for-devs/contribute', + label: 'Contribute', + }, + { + type: 'doc', + id: 'learn-more/faq', + label: 'FAQ', + }, + { + type: 'doc', + label: 'Audit reports', + id: 'audit-reports', + }, + { + type: 'category', + label: 'Third-party docs', + collapsed: true, + items: [ + { + type: 'category', + label: 'Oracles', + collapsed: true, + items: [ + { + type: 'doc', + id: 'for-devs/oracles/api3/api3', + }, + { + type: 'doc', + id: 'for-devs/oracles/chainlink/chainlink', + }, + { + type: 'doc', + id: 'for-devs/oracles/chronicle/chronicle', + }, + { + type: 'doc', + id: 'for-devs/oracles/ora/ora', + }, + { + type: 'doc', + id: 'for-devs/oracles/supra/supras-price-feed', + }, + { + type: 'doc', + id: 'for-devs/oracles/supra/supras-vrf', + }, + { + type: 'doc', + id: 'for-devs/oracles/trellor/trellor', + }, + ], + }, + { + type: 'autogenerated', + dirName: 'for-devs/third-party-docs', + }, + ], + }, + { + type: 'link', + label: 'DAO docs', + href: 'https://docs.arbitrum.foundation/gentle-intro-dao-governance', + }, + { + type: 'link', + label: 'Prysm docs', + href: 'https://www.offchainlabs.com/prysm/docs', + }, + ], + // Run Arbitrum Chain sidebar + runArbitrumChainSidebar: [ + { + type: 'category', + label: 'Run an Arbitrum (Orbit) chain', + collapsed: false, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/a-gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/aep-license', + label: 'Arbitrum chain licensing', + }, + { + type: 'category', + label: 'Features', + collapsed: true, + items: [ + { + type: 'category', + label: 'Common', + collapsed: true, + items: [ + { + type: 'category', + label: 'Data availability', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/data-availability/choose-rollup', + label: `Rollup`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/data-availability/choose-anytrust', + label: `AnyTrust`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/data-availability/choose-alt-da', + label: `Alt-DA`, + }, + ], + }, + { + type: 'category', + label: 'Gas and fees', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/gas-and-fees/choose-custom-gas-token', + label: `Custom gas token`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/gas-and-fees/choose-fee-rebates', + label: `Fee rebates`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/gas-and-fees/choose-native-eth', + label: `Native ETH`, + }, + ], + }, + { + type: 'category', + label: 'Validation and security', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/validation-and-security/choose-bold', + label: `BoLD`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/validation-and-security/choose-challenge-period', + label: `Custom challenge period`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/validation-and-security/choose-permissioned-validators', + label: `Permissioned validators`, + }, + ], + }, + { + type: 'category', + label: 'MEV', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/mev/choose-timeboost', + label: `Timeboost`, + }, + ], + }, + { + type: 'category', + label: 'UX', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/ux/choose-fast-withdrawals', + label: `Fast withdrawals`, + }, + ], + }, + { + type: 'category', + label: 'AEP fees', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/features/common/configure-aep/configure-aep-fees', + label: `AEP fees`, + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Advanced', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/features/advanced/choose-arbos-version', + label: `ArbOS version`, + }, + { + type: 'doc', + label: 'Precompiles', + id: 'launch-arbitrum-chain/features/advanced/choose-chain-precompiles', + }, + { + type: 'doc', + label: 'Chain behavior', + id: 'launch-arbitrum-chain/features/advanced/choose-custom-behavior', + }, + { + type: 'doc', + label: 'Delayed inbox finality', + id: 'launch-arbitrum-chain/features/advanced/choose-custom-delay-inbox-finality', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Configure your chain', + collapsed: true, + items: [ + { + type: 'category', + label: 'Common features', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust', + label: `Configure a custom AnyTrust gas token`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup', + label: `Configure a custom Rollup gas token`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/customizable-challenge-period', + label: 'Customize the challenge period', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/fee-management', + label: `Manage the fee parameters`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/enable-post-4844-blobs', + label: `Enable blob transactions`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/arbitrum-chain-finality', + label: `Configure delayed inbox finality`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/batch-posting-assertion-control', + label: 'Configure Batch Poster and Assertion Control', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/gas-optimization-tools', + label: `Gas optimization tools`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains', + label: 'BoLD for Arbitrum chains', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/timeboost-for-arbitrum-chains', + label: 'Timeboost for Arbitrum chains', + }, + { + type: 'category', + label: 'Data Availability Committees', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/get-started', + label: 'Get started', + }, + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/deploy-das', + label: 'Deploy a Data Availability Server (DAS)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/deploy-mirror-das', + label: 'Deploy a mirror Data Availability Server', + }, + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/configure-dac', + label: 'Configure a Data Availability Committee (DAC)', + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/how-tos/customize-deployment-configuration', + label: `Customize your chain's deployment`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/reference/additional-configuration-parameters', + label: `Additional configuration parameters`, + }, + ], + }, + { + type: 'category', + label: 'Advanced features', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/fast-withdrawals', + label: `Enable fast withdrawals`, + }, + { + type: 'doc', + label: 'Use Layer Leap', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/layer-leap', + }, + { + type: 'category', + label: 'Configure AEP fee routing', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/aep-fee-router-introduction', + label: `AEP fee router overview`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router', + label: `Set up AEP fee router`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/calculate-aep-fees', + label: `Calculate AEP license fees`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/reporting-on-fees', + label: `Report your fees`, + }, + ], + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Deploy an Arbitrum chain', + collapsed: true, + items: [ + { + type: 'category', + label: 'Deploy a production chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/arbitrum-chain-sdk-introduction', + label: `Overview`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain', + label: `Deploy an Arbitrum chain`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/how-tos/arbitrum-chain-sdk-preparing-node-config', + label: `Generate the node config file`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-token-bridge', + label: `Deploy a token bridge`, + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/canonical-factory-contracts', + label: 'Canonical factory contracts', + }, + ], + }, + { + type: 'category', + label: 'Maintain your chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/ownership-structure-access-control', + label: 'Ownership structure and access control', + }, + { + type: 'category', + label: 'ArbOS', + collapsed: true, + items: [ + { + type: 'html', + value: + 'ArbOS software releases ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/arbos-upgrade', + label: `Upgrade ArbOS`, + }, + ], + }, + { + type: 'category', + label: 'Guidance', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/guidance/state-growth', + label: `Manage gas state growth`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/guidance/state-size-limit', + label: `Manage gas speed limit`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/guidance/post-launch-contract-deployments', + label: `Post-launch deployments`, + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/monitoring-tools-and-considerations', + label: 'Monitoring tools and considerations', + }, + ], + }, + { + type: 'category', + label: 'Customize your chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/customize-your-chain/customize-precompile', + label: `Customize your chain's precompiles`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/customize-your-chain/customize-stf', + label: `Customize your chain's behavior`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/customize-your-chain/customize-arbos', + label: `Customize ArbOS version`, + }, + ], + }, + { + type: 'category', + label: 'Migrate your chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/migrate-from-another-stack', + label: 'Migrate from another stack', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/migrate-between-raases', + label: 'Migrate between RaaSes', + }, + ], + }, + { + type: 'category', + label: 'Third-party integrations and features', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/third-party-integrations/bridged-usdc-standard', + label: `Implement Circle bridged USDC`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/third-party-integrations/third-party-providers', + label: 'Third-party infrastructure providers', + }, + ], + }, + { + type: 'category', + label: 'Run a node for an Arbitrum chain', + collapsed: true, + items: [ + { + type: 'html', + value: + 'Run a full node ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'html', + value: + 'Run a validator ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'html', + value: + 'Run a sequencer node ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'html', + value: + 'Run high-availability sequencer nodes ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/ecosystem-support/add-arbitrum-chain-to-bridge-ui', + label: `Add your chain to the bridge`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/concepts/public-preview-expectations', + label: 'Public preview', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/faq-troubleshooting/troubleshooting-building-arbitrum-chain', + label: 'FAQ', + }, + ], + }, + + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Run Node sidebar + runNodeSidebar: [ + { + type: 'category', + label: 'Run an Arbitrum node', + collapsed: false, + link: { + type: 'doc', + id: 'run-arbitrum-node/overview', + }, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-full-node', + label: 'Run a full node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-local-full-chain-simulation', + label: 'Run a local full chain simulation', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-nitro-dev-node', + label: 'Run a local dev node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers', + label: 'L1 Ethereum RPC providers', + }, + { + type: 'doc', + id: 'run-arbitrum-node/data-availability', + label: 'Data Availability', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-feed-relay', + label: 'Run a feed relay', + }, + { + type: 'doc', + id: 'run-arbitrum-node/beacon-nodes-historical-blobs', + label: 'Historical blobs', + }, + { + type: 'html', + value: + 'Data Availability Committees ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'category', + label: 'ArbOS software releases', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos50', + label: 'Dia (ArbOS 50)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos40', + label: 'Callisto (ArbOS 40)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos32', + label: 'Bianca (ArbOS 32)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos20', + label: 'Atlas (ArbOS 20)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos11', + label: 'ArbOS 11', + }, + ], + }, + { + type: 'category', + label: 'More node types', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-archive-node', + label: 'Run an archive node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-validator-node', + label: 'Run a validator', + }, + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-split-validator-node', + label: 'Run a split validator', + }, + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-classic-node', + label: 'Run a Classic node', + }, + ], + }, + { + type: 'category', + label: 'Sequencer', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/run-sequencer-node', + label: 'Run a sequencer node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/read-sequencer-feed', + label: 'Read the sequencer feed', + }, + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/run-sequencer-coordination-manager', + label: 'Run a Sequencer Coordination Manager (SQM)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/high-availability-sequencer-docs', + label: 'Run high availability sequencer nodes', + }, + ], + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/build-nitro-locally', + label: 'Build Nitro locally', + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/migrate-state-and-history-from-classic', + label: 'Migrate to Nitro from Classic', + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/nitro-database-snapshots', + label: 'Database snapshots', + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/how-to-convert-databases-from-leveldb-to-pebble', + label: 'Convert databases from LevelDB to Pebble', + }, + , + { + type: 'doc', + id: 'run-arbitrum-node/troubleshooting', + label: 'Troubleshooting', + }, + { + type: 'doc', + label: 'FAQ', + id: 'node-running/faq', + }, + ], + }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Bridge sidebar + bridgeSidebar: [ + { + type: 'category', + label: 'Arbitrum bridge', + collapsed: false, + items: [ + { + type: 'doc', + id: 'arbitrum-bridge/quickstart', + label: 'Quickstart', + }, + { + type: 'doc', + id: 'arbitrum-bridge/usdc-arbitrum-one', + label: 'USDC on Arbitrum One', + }, + { + type: 'doc', + id: 'arbitrum-bridge/embedded-bridge-widget', + label: 'Embedded bridge widget', + }, + { + type: 'doc', + id: 'arbitrum-bridge/troubleshooting', + label: 'Troubleshooting', + }, + ], + }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // How it Works sidebar + howItWorksSidebar: [ + { + type: 'category', + label: 'How Arbitrum works', + collapsed: false, + items: [ + { + type: 'doc', + id: 'how-arbitrum-works/inside-arbitrum-nitro', + label: 'Inside Arbitrum Nitro', + }, + { + type: 'html', + value: + 'Nitro whitepaper', + }, + { + type: 'category', + label: 'Deep dives', + items: [ + { + type: 'doc', + label: 'AnyTrust', + id: 'how-arbitrum-works/deep-dives/anytrust-protocol', + }, + { + type: 'doc', + label: 'ArbOS', + id: 'how-arbitrum-works/deep-dives/arbos', + }, + { + type: 'doc', + label: 'Assertions', + id: 'how-arbitrum-works/deep-dives/assertions', + }, + { + type: 'doc', + label: 'Parent to Child chain Messaging', + id: 'how-arbitrum-works/deep-dives/l1-to-l2-messaging', + }, + { + type: 'doc', + label: 'Child to Parent chain Messaging', + id: 'how-arbitrum-works/deep-dives/l2-to-l1-messaging', + }, + { + type: 'doc', + label: 'Geth', + id: 'how-arbitrum-works/deep-dives/geth', + }, + { + type: 'doc', + label: 'Parent chain pricing', + id: 'how-arbitrum-works/deep-dives/parent-chain-pricing', + }, + { + type: 'doc', + label: 'Sequencer', + id: 'how-arbitrum-works/deep-dives/sequencer', + }, + { + type: 'doc', + label: 'STF', + id: 'how-arbitrum-works/deep-dives/stf-gentle-intro', + }, + { + type: 'doc', + label: 'STF inputs', + id: 'how-arbitrum-works/deep-dives/stf-inputs', + }, + { + type: 'doc', + label: 'Transaction lifecycle', + id: 'how-arbitrum-works/deep-dives/transaction-lifecycle', + }, + { + type: 'doc', + label: 'Gas and fees', + id: 'how-arbitrum-works/deep-dives/gas-and-fees', + }, + ], + }, + { + type: 'category', + label: 'The BoLD dispute protocol', + items: [ + { + type: 'doc', + id: 'how-arbitrum-works/bold/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'link', + href: 'https://github.com/offchainlabs/bold-validator-starter-kit', + label: 'Deploy a validator on testnet', + }, + { + type: 'link', + href: 'https://arxiv.org/abs/2404.10491', + label: 'BoLD Whitepaper', + }, + { + type: 'doc', + id: 'how-arbitrum-works/bold/bold-technical-deep-dive', + label: 'Technical deep dive', + }, + { + type: 'doc', + id: 'how-arbitrum-works/bold/bold-economics-of-disputes', + label: 'Economics of disputes', + }, + { + type: 'link', + href: 'https://github.com/OffchainLabs/bold', + label: 'Specification on Github', + }, + { + type: 'link', + href: 'https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf', + label: 'Audit Report by Trail of Bits', + }, + { + type: 'link', + href: 'https://code4rena.com/reports/2024-05-arbitrum-foundation', + label: 'Audit Report by Code4rena', + }, + ], + }, + { + type: 'category', + label: 'Timeboost', + items: [ + { + type: 'doc', + id: 'how-arbitrum-works/timeboost/gentle-introduction', + label: 'Public preview', + }, + { + type: 'doc', + label: 'Use Timeboost', + id: 'how-arbitrum-works/timeboost/how-to-use-timeboost', + }, + { + type: 'doc', + label: 'Troubleshoot Timeboost', + id: 'how-arbitrum-works/timeboost/troubleshoot-timeboost', + }, + { + type: 'doc', + label: 'Timeboost FAQ', + id: 'how-arbitrum-works/timeboost/timeboost-faq', + }, + { + type: 'link', + href: 'https://github.com/OffchainLabs/timeboost-design/blob/main/research_spec.md', + label: 'Specification: Timeboost', + }, + { + type: 'link', + href: 'https://github.com/OffchainLabs/decentralized-timeboost-spec', + label: 'Specification: Timeboost with decentralized sequencing', + }, + { + type: 'link', + href: 'https://arxiv.org/abs/2306.02179', + label: 'White paper: Timeboost', + }, + ], + }, + ], + }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Unified build apps sidebar (combines build apps and Stylus) + buildAppsSidebar: [ + { + type: 'category', + label: 'Build apps with Solidity', + collapsed: false, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Quickstart', + }, + { + type: 'doc', + label: 'Estimate gas', + id: 'build-decentralized-apps/how-to-estimate-gas', + }, + { + type: 'doc', + label: 'Chains and testnets', + id: 'build-decentralized-apps/public-chains', + }, + { + type: 'doc', + label: 'Cross-chain messaging', + id: 'build-decentralized-apps/cross-chain-messaging', + }, + { + type: 'doc', + id: 'build-decentralized-apps/custom-gas-token-sdk', + label: 'Custom gas token SDK', + }, + { + type: 'category', + label: 'Arbitrum vs Ethereum', + items: [ + { + type: 'doc', + label: 'Comparison overview', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + }, + { + type: 'doc', + label: 'Block gas limit, numbers and time', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + }, + { + type: 'doc', + label: 'RPC methods', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + }, + { + type: 'doc', + label: 'Solidity support', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + }, + ], + }, + { + type: 'doc', + label: 'Oracles', + id: 'build-decentralized-apps/oracles/overview-oracles', + }, + { + type: 'category', + label: 'Precompiles', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/precompiles/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/precompiles/reference', + }, + ], + }, + { + type: 'category', + label: 'NodeInterface', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/nodeinterface/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/nodeinterface/reference', + }, + ], + }, + { + type: 'category', + label: 'Token bridging', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/token-bridging/overview', + }, + { + type: 'doc', + label: 'ETH bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + }, + { + type: 'doc', + label: 'ERC-20 token bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + }, + { + type: 'category', + label: 'Bridge tokens programmatically', + items: [ + { + type: 'doc', + label: 'Get started', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', + }, + { + type: 'doc', + label: 'Use the standard gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', + }, + { + type: 'doc', + label: 'Use the generic-custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', + }, + { + type: 'doc', + label: 'Use the custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/reference/node-providers', + label: 'RPC endpoints and providers', + }, + { + type: 'doc', + label: 'Smart contract addresses', + id: 'build-decentralized-apps/reference/contract-addresses', + }, + { + type: 'doc', + label: 'Chain parameters', + id: 'build-decentralized-apps/reference/chain-params', + }, + { + type: 'doc', + label: 'Development frameworks', + id: 'build-decentralized-apps/reference/development-frameworks', + }, + { + type: 'doc', + label: 'Web3 libraries and tools', + id: 'build-decentralized-apps/reference/web3-libraries-tools', + }, + { + type: 'doc', + label: 'Monitoring tools and block explorers', + id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + }, + { + type: 'doc', + label: 'Debugging tools', + id: 'build-decentralized-apps/reference/debugging-tools', + }, + + { + type: 'doc', + id: 'build-decentralized-apps/reference/mainnet-risks', + label: 'Mainnet risks', + }, + ], + }, + { + type: 'doc', + label: 'Troubleshooting', + id: 'for-devs/troubleshooting-building', + }, + { + type: 'category', + label: 'Arbitrum SDK', + items: sdkSidebar.sdkSidebar, + }, + { + type: 'link', + label: 'Tutorials', + href: 'https://github.com/OffchainLabs/arbitrum-tutorials', + }, + ], + }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Solidity-focused sidebar (only shows Solidity content) + buildSoliditySidebar: [ + { + type: 'category', + label: 'Build apps with Solidity', + collapsed: false, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Quickstart', + }, + { + type: 'doc', + label: 'Estimate gas', + id: 'build-decentralized-apps/how-to-estimate-gas', + }, + { + type: 'doc', + label: 'Chains and testnets', + id: 'build-decentralized-apps/public-chains', + }, + { + type: 'doc', + label: 'Cross-chain messaging', + id: 'build-decentralized-apps/cross-chain-messaging', + }, + { + type: 'doc', + id: 'build-decentralized-apps/custom-gas-token-sdk', + label: 'Custom gas token SDK', + }, + { + type: 'category', + label: 'Arbitrum vs Ethereum', + items: [ + { + type: 'doc', + label: 'Comparison overview', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + }, + { + type: 'doc', + label: 'Block gas limit, numbers and time', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + }, + { + type: 'doc', + label: 'RPC methods', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + }, + { + type: 'doc', + label: 'Solidity support', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + }, + ], + }, + { + type: 'doc', + label: 'Oracles', + id: 'build-decentralized-apps/oracles/overview-oracles', + }, + { + type: 'category', + label: 'Precompiles', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/precompiles/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/precompiles/reference', + }, + ], + }, + { + type: 'category', + label: 'NodeInterface', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/nodeinterface/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/nodeinterface/reference', + }, + ], + }, + { + type: 'category', + label: 'Token bridging', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/token-bridging/overview', + }, + { + type: 'doc', + label: 'ETH bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + }, + { + type: 'doc', + label: 'ERC-20 token bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + }, + { + type: 'category', + label: 'Bridge tokens programmatically', + items: [ + { + type: 'doc', + label: 'Get started', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', + }, + { + type: 'doc', + label: 'Use the standard gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', + }, + { + type: 'doc', + label: 'Use the generic-custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', + }, + { + type: 'doc', + label: 'Use the custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/reference/node-providers', + label: 'RPC endpoints and providers', + }, + { + type: 'doc', + label: 'Smart contract addresses', + id: 'build-decentralized-apps/reference/contract-addresses', + }, + { + type: 'doc', + label: 'Chain parameters', + id: 'build-decentralized-apps/reference/chain-params', + }, + { + type: 'doc', + label: 'Development frameworks', + id: 'build-decentralized-apps/reference/development-frameworks', + }, + { + type: 'doc', + label: 'Web3 libraries and tools', + id: 'build-decentralized-apps/reference/web3-libraries-tools', + }, + { + type: 'doc', + label: 'Monitoring tools and block explorers', + id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + }, + { + type: 'doc', + label: 'Debugging tools', + id: 'build-decentralized-apps/reference/debugging-tools', + }, + + { + type: 'doc', + id: 'build-decentralized-apps/reference/mainnet-risks', + label: 'Mainnet risks', + }, + ], + }, + { + type: 'doc', + label: 'Troubleshooting', + id: 'for-devs/troubleshooting-building', + }, + { + type: 'category', + label: 'Arbitrum SDK', + items: sdkSidebar.sdkSidebar, + }, + { + type: 'link', + label: 'Tutorials', + href: 'https://github.com/OffchainLabs/arbitrum-tutorials', + }, + ], + }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Stylus-focused sidebar (only shows Stylus content) + buildStylusSidebar: [ + { + type: 'category', + label: 'Build apps with Stylus', + collapsed: false, + items: [ + { + type: 'doc', + id: 'stylus/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'category', + label: 'Stylus Rust SDK', + collapsed: true, + link: { + type: 'doc', + id: 'stylus/reference/overview', + }, + + items: [ + { + type: 'doc', + id: 'stylus/reference/project-structure', + label: 'Structure of a project', + }, + { + type: 'doc', + id: 'stylus/reference/contracts', + label: 'Structure of a contracts', + }, + { + type: 'category', + label: 'Data types', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/reference/data-types/primitives', + label: 'Primitives', + }, + { + type: 'doc', + id: 'stylus/reference/data-types/compound-types', + label: 'Compound types', + }, + { + type: 'doc', + id: 'stylus/reference/data-types/storage', + label: 'Storage', + }, + { + type: 'doc', + id: 'stylus/reference/data-types/conversions-between-types', + label: 'Conversions Between Types', + }, + ], + }, + { + type: 'doc', + id: 'stylus/reference/global-variables-and-functions', + label: 'Global variables and functions', + }, + { + type: 'doc', + id: 'stylus/how-tos/testing-contracts', + label: 'Writing Tests', + }, + { + type: 'category', + label: 'Advanced', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/advanced/solidity-differences', + label: 'Solidity differences', + }, + { + type: 'doc', + id: 'stylus/advanced/recommended-libraries', + label: 'Recommended packages', + }, + { + type: 'doc', + id: 'stylus/advanced/minimal-entrypoint-contracts', + label: 'Minimal entrypoint contracts', + }, + { + type: 'doc', + id: 'stylus/advanced/hostio-exports', + label: 'Hostio exports', + }, + ], + }, + { + type: 'category', + label: 'VM Concepts', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/concepts/webassembly', + label: 'Webassembly', + }, + { + type: 'doc', + id: 'stylus/concepts/evm-differences', + label: 'EVM differences', + }, + { + type: 'doc', + id: 'stylus/concepts/activation', + label: 'Activation', + }, + { + type: 'doc', + id: 'stylus/how-tos/caching-contracts', + label: 'Caching Strategy', + }, + ], + }, + { + type: 'category', + label: 'Reference', + collapsed: true, + items: [ + { + type: 'link', + label: 'Stylus by Example', + href: 'https://stylus-by-example.org/', + }, + { + type: 'link', + label: 'Cargo Stylus CLI GitHub', + href: 'https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus', + }, + { + type: 'link', + label: 'Rust SDK Crate', + href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', + }, + { + type: 'link', + label: 'Source Code Repository', + href: 'https://github.com/OffchainLabs/stylus-sdk-rs', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Using Stylus CLI', + collapsed: true, + link: { + type: 'doc', + id: 'stylus/using-cli', + }, + items: [ + { + type: 'doc', + id: 'stylus/how-tos/check-and-deploy', + label: 'Check and deploy', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts', + label: 'Verify contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/exporting-abi', + label: 'Exporting ABI', + }, + { + type: 'doc', + id: 'stylus/how-tos/importing-interfaces', + label: 'Importing interfaces', + }, + { + type: 'doc', + id: 'stylus/how-tos/debugging-tx', + label: 'Debugging with replay', + }, + { + type: 'doc', + id: 'stylus/how-tos/optimizing-binaries', + label: 'Optimizing WASM binary size', + }, + { + type: 'doc', + id: 'stylus/how-tos/deploying-non-rust-wasm-contracts', + label: 'Deploying non-Rust WASM contracts', + }, + ], + }, + { + type: 'doc', + id: 'stylus/troubleshooting-building-stylus', + label: 'FAQ', + }, + ], + }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, + ], + + // Notices sidebar + noticeSidebar: [ + { + type: 'doc', + id: 'notices/fusaka-upgrade-notice', + label: 'Upgrade for the Fusaka transition', + }, + ], +}; + +module.exports = sidebars; + +// note RE html sidebar links: +// because the linked page lives in multiple sidebar sections, we pick one to be the "canonical" location for the page in the sidebar +// if we link to them both via id or standard href, multiple sections of the sidebar will be opened at once when the user visits this page; we don't want that +// if we use a fully qualified link, the remote/published page will display when visiting from localhost or preview deployments +// we also want to include a unicode arrow to indicate that we're routing the user to another section, in a way that's distinct from the icon that indicates "this href pulls you out of docs" diff --git a/src/components/FloatingHoverModal/index.js b/src/components/FloatingHoverModal/index.js index bf86b3824e..249e17aaf7 100644 --- a/src/components/FloatingHoverModal/index.js +++ b/src/components/FloatingHoverModal/index.js @@ -13,7 +13,6 @@ import { autoUpdate, useMergeRefs, } from '@floating-ui/react'; -import { MDXProvider } from '@mdx-js/react'; // Remove Link import - we'll use a span instead to avoid Docusaurus broken link detection (Docusaurus's build will fail if a points to a non-existent page) import './styles.css'; @@ -32,6 +31,7 @@ import ConfigPermissionedValidators from '@site/docs/launch-arbitrum-chain/featu import ConfigL1ChallengePeriod from '@site/docs/launch-arbitrum-chain/partials/config-l1-challenge-period.mdx'; import ConfigForceInclusion from '@site/docs/launch-arbitrum-chain/partials/config-force-inclusion.mdx'; import ConfigAccountAbstraction from '@site/docs/launch-arbitrum-chain/partials/config-account-abstraction.mdx'; +import ConfigChallengePeriod from '@site/docs/launch-arbitrum-chain/partials/config-challenge-period-l1.mdx'; import ConfigCustomizableGovernance from '@site/docs/launch-arbitrum-chain/partials/config-customizable-governance.mdx'; import ConfigDataPostingCosts from '@site/docs/launch-arbitrum-chain/partials/config-data-posting-costs.mdx'; import ConfigEVMCompatibility from '@site/docs/launch-arbitrum-chain/partials/config-evm-compatbility.mdx'; @@ -53,35 +53,13 @@ const contentMap = { 'config-l1-challenge-period': ConfigL1ChallengePeriod, 'config-force-inclusion': ConfigForceInclusion, 'config-account-abstraction': ConfigAccountAbstraction, + 'config-challenge-period-l1': ConfigChallengePeriod, 'config-customizable-governance': ConfigCustomizableGovernance, 'config-data-posting-costs': ConfigDataPostingCosts, 'config-evm-compatibility': ConfigEVMCompatibility, 'config-other-language-support': ConfigOtherLanguageSupport, }; -// MDX components for proper rendering -const mdxComponents = { - h1: ({ children }) =>

{children}

, - h2: ({ children }) =>

{children}

, - p: ({ children }) =>

{children}

, - ul: ({ children }) =>
    {children}
, - ol: ({ children }) =>
    {children}
, - li: ({ children }) =>
  • {children}
  • , - strong: ({ children }) => {children}, - code: ({ children }) => {children}, - table: ({ children }) => {children}
    , - thead: ({ children }) => {children}, - tbody: ({ children }) => {children}, - tr: ({ children }) => {children}, - th: ({ children }) => {children}, - td: ({ children }) => {children}, - a: ({ children, href }) => ( - - {children} - - ), -}; - export function FloatingHoverModal({ href, children }) { const [isOpen, setIsOpen] = useState(false); @@ -149,9 +127,7 @@ export function FloatingHoverModal({ href, children }) {
    {ContentComponent ? ( - - - + ) : (

    Content Not Available

    diff --git a/src/components/GenerateTroubleshootingReportWidget.js b/src/components/GenerateTroubleshootingReportWidget.js index 8490afee01..799813f486 100644 --- a/src/components/GenerateTroubleshootingReportWidget.js +++ b/src/components/GenerateTroubleshootingReportWidget.js @@ -1,5 +1,4 @@ import React from 'react'; -import BrowserOnly from '@docusaurus/BrowserOnly'; export const GenerateTroubleshootingReportWidget = () => { let appendConfigDetailsToOutput = function (output) { @@ -87,11 +86,9 @@ export const GenerateTroubleshootingReportWidget = () => { }, 100); }; - return ( - - {() => { - bindButton(); - }} - - ); + React.useEffect(() => { + bindButton(); + }, []); + + return null; }; diff --git a/src/components/HeaderBadges/index.tsx b/src/components/HeaderBadges/index.tsx index 92aedce7ac..fb3f9695b4 100644 --- a/src/components/HeaderBadges/index.tsx +++ b/src/components/HeaderBadges/index.tsx @@ -1,25 +1,31 @@ import React from 'react'; -import BrowserOnly from '@docusaurus/BrowserOnly'; export const HeaderBadges = () => { + const [href, setHref] = React.useState(''); + + React.useEffect(() => { + // Client-side only: build the GitHub issue URL + const pathname = new URL(window.location.href).pathname; + const url = window.location.href; + setHref( + `https://github.com/OffchainLabs/arbitrum-docs/issues/new?title=Docs update request: ${pathname}&body=Source: ${url}%0A%0ARequest: (how can we help?)%0A%0APsst, this issue will be closed with a templated response if it isn't a documentation update request.`, + ); + }, []); + + // Always render the structure, just update href on client return ( - - {() => ( - - )} - + ); }; diff --git a/src/components/InteractiveDiagrams/Timeboost/CentralizedAuction/Modal.tsx b/src/components/InteractiveDiagrams/Timeboost/CentralizedAuction/Modal.tsx index 49c33c4324..a5e1098bcb 100644 --- a/src/components/InteractiveDiagrams/Timeboost/CentralizedAuction/Modal.tsx +++ b/src/components/InteractiveDiagrams/Timeboost/CentralizedAuction/Modal.tsx @@ -154,9 +154,7 @@ export function Modal({ number }: { number: number }) { - - - +
    ), diff --git a/src/components/MultiDimensionalContentWidget.js b/src/components/MultiDimensionalContentWidget.js index 3ae518ea84..0ad799bdbf 100644 --- a/src/components/MultiDimensionalContentWidget.js +++ b/src/components/MultiDimensionalContentWidget.js @@ -1,5 +1,4 @@ import React from 'react'; -import BrowserOnly from '@docusaurus/BrowserOnly'; export const MultiDimensionalContentWidget = () => { let getAllTabElements = function () { @@ -99,11 +98,9 @@ export const MultiDimensionalContentWidget = () => { }, 100); }; - return ( - - {() => { - bindTabs(); - }} - - ); + React.useEffect(() => { + bindTabs(); + }, []); + + return null; }; diff --git a/src/css/custom.css b/src/css/custom.css index dbe0b5f53f..e028f61aba 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -98,3 +98,24 @@ .wrapcode code { white-space: pre-wrap; } + +/* Details/Summary collapsible elements - border radius override */ +details, +details.alert { + border-radius: 0.5rem !important; + overflow: hidden; + border: none !important; +} + +details[open], +details.alert[open] { + border-radius: 0.5rem !important; + overflow: hidden; + border: none !important; +} + +/* Ensure summary element also respects border radius at top */ +details > summary { + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; +} diff --git a/static/img/stylus-activation-process.png b/static/img/stylus-activation-process.png new file mode 100644 index 0000000000..204af82a76 Binary files /dev/null and b/static/img/stylus-activation-process.png differ diff --git a/static/img/stylus-call-decision-tree.png b/static/img/stylus-call-decision-tree.png new file mode 100644 index 0000000000..e2c35e47ab Binary files /dev/null and b/static/img/stylus-call-decision-tree.png differ diff --git a/static/img/stylus-contract-lifecycle.png b/static/img/stylus-contract-lifecycle.png new file mode 100644 index 0000000000..220986189a Binary files /dev/null and b/static/img/stylus-contract-lifecycle.png differ diff --git a/static/img/stylus-gas-comparison.png b/static/img/stylus-gas-comparison.png new file mode 100644 index 0000000000..40a958ffc2 Binary files /dev/null and b/static/img/stylus-gas-comparison.png differ diff --git a/static/img/stylus-learning-paths.png b/static/img/stylus-learning-paths.png new file mode 100644 index 0000000000..c403889076 Binary files /dev/null and b/static/img/stylus-learning-paths.png differ diff --git a/static/img/stylus-multivm.jpg b/static/img/stylus-multivm.jpg index ee2288c8cc..89f67ece76 100644 Binary files a/static/img/stylus-multivm.jpg and b/static/img/stylus-multivm.jpg differ diff --git a/static/img/stylus-quickstart-journey.png b/static/img/stylus-quickstart-journey.png new file mode 100644 index 0000000000..a7f34a691c Binary files /dev/null and b/static/img/stylus-quickstart-journey.png differ diff --git a/static/img/stylus-storage-slots.png b/static/img/stylus-storage-slots.png new file mode 100644 index 0000000000..6954ae70d6 Binary files /dev/null and b/static/img/stylus-storage-slots.png differ diff --git a/static/img/stylus-storage-types-hierarchy.png b/static/img/stylus-storage-types-hierarchy.png new file mode 100644 index 0000000000..8487ba7463 Binary files /dev/null and b/static/img/stylus-storage-types-hierarchy.png differ diff --git a/static/img/stylus-wasm-compile.png b/static/img/stylus-wasm-compile.png new file mode 100644 index 0000000000..c72ea96a37 Binary files /dev/null and b/static/img/stylus-wasm-compile.png differ diff --git a/static/img/stylus-wasm-deploy.png b/static/img/stylus-wasm-deploy.png new file mode 100644 index 0000000000..26a4644ec9 Binary files /dev/null and b/static/img/stylus-wasm-deploy.png differ diff --git a/static/img/stylus-wasm-execute.png b/static/img/stylus-wasm-execute.png new file mode 100644 index 0000000000..0910637ab1 Binary files /dev/null and b/static/img/stylus-wasm-execute.png differ diff --git a/submodules/stylus-by-example b/submodules/stylus-by-example deleted file mode 160000 index 24eb919c99..0000000000 --- a/submodules/stylus-by-example +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 24eb919c99079bada64c30db1cb2896fbada8a1f diff --git a/yarn.lock b/yarn.lock index e464b7a9b2..763f252203 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,158 +43,191 @@ resolved "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz" integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q== -"@algolia/autocomplete-core@1.9.3": - version "1.9.3" - resolved "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz" - integrity sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw== +"@ai-sdk/gateway@2.0.22": + version "2.0.22" + resolved "https://registry.yarnpkg.com/@ai-sdk/gateway/-/gateway-2.0.22.tgz#045ba030ad47728748efb60e213e9b1d6aa89ff6" + integrity sha512-6fHjDfCbjfj4vyMExuLei7ir2///E5sNwNZaobdJsJIxJjDSsjzSLGO/aUI7p9eOnB8XctDrDSF5ilwDGpi6eg== dependencies: - "@algolia/autocomplete-plugin-algolia-insights" "1.9.3" - "@algolia/autocomplete-shared" "1.9.3" + "@ai-sdk/provider" "2.0.0" + "@ai-sdk/provider-utils" "3.0.19" + "@vercel/oidc" "3.0.5" -"@algolia/autocomplete-plugin-algolia-insights@1.9.3": - version "1.9.3" - resolved "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz" - integrity sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg== +"@ai-sdk/provider-utils@3.0.19": + version "3.0.19" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz#065e4ffe287ec536b882fdcdff0bd38c250a4873" + integrity sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA== dependencies: - "@algolia/autocomplete-shared" "1.9.3" + "@ai-sdk/provider" "2.0.0" + "@standard-schema/spec" "^1.0.0" + eventsource-parser "^3.0.6" -"@algolia/autocomplete-preset-algolia@1.9.3": - version "1.9.3" - resolved "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz" - integrity sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA== +"@ai-sdk/provider@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-2.0.0.tgz#b853c739d523b33675bc74b6c506b2c690bc602b" + integrity sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA== dependencies: - "@algolia/autocomplete-shared" "1.9.3" - -"@algolia/autocomplete-shared@1.9.3": - version "1.9.3" - resolved "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz" - integrity sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ== + json-schema "^0.4.0" -"@algolia/cache-browser-local-storage@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz" - integrity sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg== +"@ai-sdk/react@^2.0.30": + version "2.0.117" + resolved "https://registry.yarnpkg.com/@ai-sdk/react/-/react-2.0.117.tgz#383428aebacf899d61daa84031a1068465afac65" + integrity sha512-qfwz4p1ev+i/M9rsOUEe53UgzxMUz7e4wrImWdkuFrpD78MBIj53eE/LtyCeAYSCFVSz3JfIDvtdk5MjTrNcbA== dependencies: - "@algolia/cache-common" "4.23.3" + "@ai-sdk/provider-utils" "3.0.19" + ai "5.0.115" + swr "^2.2.5" + throttleit "2.1.0" -"@algolia/cache-common@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz" - integrity sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A== +"@algolia/abtesting@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@algolia/abtesting/-/abtesting-1.12.1.tgz#180474d468b09edb8be25d62f61b3c498e3a7482" + integrity sha512-Y+7e2uPe376OH5O73OB1+vR40ZhbV2kzGh/AR/dPCWguoBOp1IK0o+uZQLX+7i32RMMBEKl3pj6KVEav100Kvg== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" + +"@algolia/autocomplete-core@1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.19.2.tgz#702df67a08cb3cfe8c33ee1111ef136ec1a9e232" + integrity sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw== + dependencies: + "@algolia/autocomplete-plugin-algolia-insights" "1.19.2" + "@algolia/autocomplete-shared" "1.19.2" + +"@algolia/autocomplete-plugin-algolia-insights@1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.2.tgz#3584b625b9317e333d1ae43664d02358e175c52d" + integrity sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg== + dependencies: + "@algolia/autocomplete-shared" "1.19.2" + +"@algolia/autocomplete-shared@1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.2.tgz#c0b7b8dc30a5c65b70501640e62b009535e4578f" + integrity sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w== + +"@algolia/client-abtesting@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.46.1.tgz#5682c0e6cd14b97b9f0de3bbd3b29b651d41b650" + integrity sha512-5SWfl0UGuKxMBYlU2Y9BnlIKKEyhFU5jHE9F9jAd8nbhxZNLk0y7fXE+AZeFtyK1lkVw6O4B/e6c3XIVVCkmqw== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" + +"@algolia/client-analytics@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.46.1.tgz#3db582ad78bed7c98ed72ddff440c0d5eb09d4fe" + integrity sha512-496K6B1l/0Jvyp3MbW/YIgmm1a6nkTrKXBM7DoEy9YAOJ8GywGpa2UYjNCW1UrOTt+em1ECzDjRx7PIzTR9YvA== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" + +"@algolia/client-common@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.46.1.tgz#e6b2604e8e9f674a90d0155cbe93111fbb5e6f52" + integrity sha512-3u6AuZ1Kiss6V5JPuZfVIUYfPi8im06QBCgKqLg82GUBJ3SwhiTdSZFIEgz2mzFuitFdW1PQi3c/65zE/3FgIw== + +"@algolia/client-insights@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.46.1.tgz#0d8d20890cf7e962422da19606e1038cab3de591" + integrity sha512-LwuWjdO35HHl1rxtdn48t920Xl26Dl0SMxjxjFeAK/OwK/pIVfYjOZl/f3Pnm7Kixze+6HjpByVxEaqhTuAFaw== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" + +"@algolia/client-personalization@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.46.1.tgz#46f686159967f45bff2041ce081bcdd078f04365" + integrity sha512-6LvJAlfEsn9SVq63MYAFX2iUxztUK2Q7BVZtI1vN87lDiJ/tSVFKgKS/jBVO03A39ePxJQiFv6EKv7lmoGlWtQ== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" + +"@algolia/client-query-suggestions@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.46.1.tgz#b2ab960012b72473d9b8b80cf7021d8292fde2c4" + integrity sha512-9GLUCyGGo7YOXHcNqbzca82XYHJTbuiI6iT0FTGc0BrnV2N4OcrznUuVKic/duiLSun5gcy/G2Bciw5Sav9f9w== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" + +"@algolia/client-search@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.46.1.tgz#8c41371a2351080ae2cb3473fd26c572d8d077aa" + integrity sha512-NL76o/BoEgU4ObY5oBEC3o6KSPpuXsnSta00tAxTm1iKUWOGR34DQEKhUt8xMHhMKleUNPM/rLPFiIVtfsGU8w== + dependencies: + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" -"@algolia/cache-in-memory@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz" - integrity sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg== - dependencies: - "@algolia/cache-common" "4.23.3" +"@algolia/events@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz" + integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== -"@algolia/client-account@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz" - integrity sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA== +"@algolia/ingestion@1.46.1": + version "1.46.1" + resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.46.1.tgz#f0a5e9517784be36411866b2093d1cc6e87b6dbd" + integrity sha512-52Nc8WKC1FFXsdlXlTMl1Re/pTAbd2DiJiNdYmgHiikZcfF96G+Opx4qKiLUG1q7zp9e+ahNwXF6ED0XChMywg== dependencies: - "@algolia/client-common" "4.23.3" - "@algolia/client-search" "4.23.3" - "@algolia/transporter" "4.23.3" + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" -"@algolia/client-analytics@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz" - integrity sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA== +"@algolia/monitoring@1.46.1": + version "1.46.1" + resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.46.1.tgz#3883c6ef5807e865aaaf5d239055f5b257dcafb0" + integrity sha512-1x2/2Y/eqz6l3QcEZ8u/zMhSCpjlhePyizJd3sXrmg031HjayYT5+IxikjpqkdF7TU/deCTd/TFUcxLJ2ZHXiQ== dependencies: - "@algolia/client-common" "4.23.3" - "@algolia/client-search" "4.23.3" - "@algolia/requester-common" "4.23.3" - "@algolia/transporter" "4.23.3" + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" -"@algolia/client-common@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz" - integrity sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw== +"@algolia/recommend@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.46.1.tgz#f47050a465b5b9b8491becaa850b4ce772eef090" + integrity sha512-SSd3KlQuplxV3aRs5+Z09XilFesgpPjtCG7BGRxLTVje5hn9BLmhjO4W3gKw01INUt44Z1r0Fwx5uqnhAouunA== dependencies: - "@algolia/requester-common" "4.23.3" - "@algolia/transporter" "4.23.3" + "@algolia/client-common" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" -"@algolia/client-personalization@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz" - integrity sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g== +"@algolia/requester-browser-xhr@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.46.1.tgz#b22946a4f4ffee6ccbbbddc89ba404065db299be" + integrity sha512-3GfCwudeW6/3caKSdmOP6RXZEL4F3GiemCaXEStkTt2Re8f7NcGYAAZnGlHsCzvhlNEuDzPYdYxh4UweY8l/2w== dependencies: - "@algolia/client-common" "4.23.3" - "@algolia/requester-common" "4.23.3" - "@algolia/transporter" "4.23.3" + "@algolia/client-common" "5.46.1" -"@algolia/client-search@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz" - integrity sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw== +"@algolia/requester-fetch@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.46.1.tgz#eaaad8935b416d2e4889b6a50524d8d58979188d" + integrity sha512-JUAxYfmnLYTVtAOFxVvXJ4GDHIhMuaP7JGyZXa/nCk3P8RrN5FCNTdRyftSnxyzwSIAd8qH3CjdBS9WwxxqcHQ== dependencies: - "@algolia/client-common" "4.23.3" - "@algolia/requester-common" "4.23.3" - "@algolia/transporter" "4.23.3" - -"@algolia/events@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz" - integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== + "@algolia/client-common" "5.46.1" -"@algolia/logger-common@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz" - integrity sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g== - -"@algolia/logger-console@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz" - integrity sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A== - dependencies: - "@algolia/logger-common" "4.23.3" - -"@algolia/recommend@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz" - integrity sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w== - dependencies: - "@algolia/cache-browser-local-storage" "4.23.3" - "@algolia/cache-common" "4.23.3" - "@algolia/cache-in-memory" "4.23.3" - "@algolia/client-common" "4.23.3" - "@algolia/client-search" "4.23.3" - "@algolia/logger-common" "4.23.3" - "@algolia/logger-console" "4.23.3" - "@algolia/requester-browser-xhr" "4.23.3" - "@algolia/requester-common" "4.23.3" - "@algolia/requester-node-http" "4.23.3" - "@algolia/transporter" "4.23.3" - -"@algolia/requester-browser-xhr@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz" - integrity sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw== - dependencies: - "@algolia/requester-common" "4.23.3" - -"@algolia/requester-common@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz" - integrity sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw== - -"@algolia/requester-node-http@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz" - integrity sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA== - dependencies: - "@algolia/requester-common" "4.23.3" - -"@algolia/transporter@4.23.3": - version "4.23.3" - resolved "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz" - integrity sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ== - dependencies: - "@algolia/cache-common" "4.23.3" - "@algolia/logger-common" "4.23.3" - "@algolia/requester-common" "4.23.3" +"@algolia/requester-node-http@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.46.1.tgz#ea2399481671c5401f0493628367d3cd43c0ab7f" + integrity sha512-VwbhV1xvTGiek3d2pOS6vNBC4dtbNadyRT+i1niZpGhOJWz1XnfhxNboVbXPGAyMJYz7kDrolbDvEzIDT93uUA== + dependencies: + "@algolia/client-common" "5.46.1" "@ampproject/remapping@^2.2.0": version "2.3.0" @@ -204,18 +237,13 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@antfu/install-pkg@^0.4.1": - version "0.4.1" - resolved "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz" - integrity sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw== +"@antfu/install-pkg@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz#78fa036be1a6081b5a77a5cf59f50c7752b6ba26" + integrity sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ== dependencies: - package-manager-detector "^0.2.0" - tinyexec "^0.3.0" - -"@antfu/utils@^0.7.10": - version "0.7.10" - resolved "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz" - integrity sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww== + package-manager-detector "^1.3.0" + tinyexec "^1.0.1" "@arbitrum/sdk@^3.0.0": version "3.4.0" @@ -228,7 +256,7 @@ async-mutex "^0.4.0" ethers "^5.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2": version "7.24.2" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz" integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== @@ -2114,10 +2142,10 @@ resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783" integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A== -"@braintree/sanitize-url@^7.0.1": - version "7.1.0" - resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.0.tgz" - integrity sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg== +"@braintree/sanitize-url@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz#15e19737d946559289b915e5dad3b4c28407735e" + integrity sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw== "@chevrotain/cst-dts-gen@11.0.3": version "11.0.3" @@ -2156,92 +2184,136 @@ resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@csstools/cascade-layer-name-parser@^2.0.4": - version "2.0.4" - resolved "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.4.tgz" - integrity sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA== +"@csstools/cascade-layer-name-parser@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz#43f962bebead0052a9fed1a2deeb11f85efcbc72" + integrity sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A== -"@csstools/color-helpers@^5.0.1": - version "5.0.1" - resolved "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz" - integrity sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA== +"@csstools/color-helpers@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz#106c54c808cabfd1ab4c602d8505ee584c2996ef" + integrity sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA== -"@csstools/css-calc@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.0.tgz" - integrity sha512-X69PmFOrjTZfN5ijxtI8hZ9kRADFSLrmmQ6hgDJ272Il049WGKpDY64KhrFm/7rbWve0z81QepawzjkKlqkNGw== +"@csstools/css-calc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" + integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== -"@csstools/css-color-parser@^3.0.6": - version "3.0.6" - resolved "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.6.tgz" - integrity sha512-S/IjXqTHdpI4EtzGoNCHfqraXF37x12ZZHA1Lk7zoT5pm2lMjFuqhX/89L7dqX4CcMacKK+6ZCs5TmEGb/+wKw== +"@csstools/css-color-parser@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz#4e386af3a99dd36c46fef013cfe4c1c341eed6f0" + integrity sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA== dependencies: - "@csstools/color-helpers" "^5.0.1" - "@csstools/css-calc" "^2.1.0" + "@csstools/color-helpers" "^5.1.0" + "@csstools/css-calc" "^2.1.4" + +"@csstools/css-parser-algorithms@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" + integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== -"@csstools/css-parser-algorithms@^3.0.4": +"@csstools/css-tokenizer@^3.0.4": version "3.0.4" - resolved "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz" - integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" + integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== -"@csstools/css-tokenizer@^3.0.3": - version "3.0.3" - resolved "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz" - integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== +"@csstools/media-query-list-parser@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz#7aec77bcb89c2da80ef207e73f474ef9e1b3cdf1" + integrity sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ== -"@csstools/media-query-list-parser@^4.0.2": - version "4.0.2" - resolved "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz" - integrity sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A== +"@csstools/postcss-alpha-function@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-alpha-function/-/postcss-alpha-function-1.0.1.tgz#7989605711de7831bc7cd75b94c9b5bac9c3728e" + integrity sha512-isfLLwksH3yHkFXfCI2Gcaqg7wGGHZZwunoJzEZk0yKYIokgre6hYVFibKL3SYAoR1kBXova8LB+JoO5vZzi9w== + dependencies: + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" -"@csstools/postcss-cascade-layers@^5.0.1": - version "5.0.1" - resolved "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.1.tgz" - integrity sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ== +"@csstools/postcss-cascade-layers@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz#dd2c70db3867b88975f2922da3bfbae7d7a2cae7" + integrity sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" -"@csstools/postcss-color-function@^4.0.6": - version "4.0.6" - resolved "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.6.tgz" - integrity sha512-EcvXfC60cTIumzpsxWuvVjb7rsJEHPvqn3jeMEBUaE3JSc4FRuP7mEQ+1eicxWmIrs3FtzMH9gR3sgA5TH+ebQ== +"@csstools/postcss-color-function-display-p3-linear@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function-display-p3-linear/-/postcss-color-function-display-p3-linear-1.0.1.tgz#3017ff5e1f65307d6083e58e93d76724fb1ebf9f" + integrity sha512-E5qusdzhlmO1TztYzDIi8XPdPoYOjoTY6HBYBCYSj+Gn4gQRBlvjgPQXzfzuPQqt8EhkC/SzPKObg4Mbn8/xMg== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-color-mix-function@^3.0.6": - version "3.0.6" - resolved "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.6.tgz" - integrity sha512-jVKdJn4+JkASYGhyPO+Wa5WXSx1+oUgaXb3JsjJn/BlrtFh5zjocCY7pwWi0nuP24V1fY7glQsxEYcYNy0dMFg== +"@csstools/postcss-color-function@^4.0.12": + version "4.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function/-/postcss-color-function-4.0.12.tgz#a7c85a98c77b522a194a1bbb00dd207f40c7a771" + integrity sha512-yx3cljQKRaSBc2hfh8rMZFZzChaFgwmO2JfFgFr1vMcF3C/uyy5I4RFIBOIWGq1D+XbKCG789CGkG6zzkLpagA== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-content-alt-text@^2.0.4": - version "2.0.4" - resolved "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.4.tgz" - integrity sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw== +"@csstools/postcss-color-mix-function@^3.0.12": + version "3.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.12.tgz#2f1ee9f8208077af069545c9bd79bb9733382c2a" + integrity sha512-4STERZfCP5Jcs13P1U5pTvI9SkgLgfMUMhdXW8IlJWkzOOOqhZIjcNhWtNJZes2nkBDsIKJ0CJtFtuaZ00moag== dependencies: - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-exponential-functions@^2.0.5": - version "2.0.5" - resolved "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.5.tgz" - integrity sha512-mi8R6dVfA2nDoKM3wcEi64I8vOYEgQVtVKCfmLHXupeLpACfGAided5ddMt5f+CnEodNu4DifuVwb0I6fQDGGQ== +"@csstools/postcss-color-mix-variadic-function-arguments@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.2.tgz#b4012b62a4eaa24d694172bb7137f9d2319cb8f2" + integrity sha512-rM67Gp9lRAkTo+X31DUqMEq+iK+EFqsidfecmhrteErxJZb6tUoJBVQca1Vn1GpDql1s1rD1pKcuYzMsg7Z1KQ== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" + +"@csstools/postcss-content-alt-text@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.8.tgz#1d52da1762893c32999ff76839e48d6ec7c7a4cb" + integrity sha512-9SfEW9QCxEpTlNMnpSqFaHyzsiRpZ5J5+KqCu1u5/eEJAWsMhzT40qf0FIbeeglEvrGRMdDzAxMIz3wqoGSb+Q== + dependencies: + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" + +"@csstools/postcss-contrast-color-function@^2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-contrast-color-function/-/postcss-contrast-color-function-2.0.12.tgz#ca46986d095c60f208d9e3f24704d199c9172637" + integrity sha512-YbwWckjK3qwKjeYz/CijgcS7WDUCtKTd8ShLztm3/i5dhh4NaqzsbYnhm4bjrpFpnLZ31jVcbK8YL77z3GBPzA== + dependencies: + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/utilities" "^2.0.0" + +"@csstools/postcss-exponential-functions@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz#fc03d1272888cb77e64cc1a7d8a33016e4f05c69" + integrity sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw== + dependencies: + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/postcss-font-format-keywords@^4.0.0": version "4.0.0" @@ -2251,67 +2323,67 @@ "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -"@csstools/postcss-gamut-mapping@^2.0.6": - version "2.0.6" - resolved "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.6.tgz" - integrity sha512-0ke7fmXfc8H+kysZz246yjirAH6JFhyX9GTlyRnM0exHO80XcA9zeJpy5pOp5zo/AZiC/q5Pf+Hw7Pd6/uAoYA== - dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - -"@csstools/postcss-gradients-interpolation-method@^5.0.6": - version "5.0.6" - resolved "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.6.tgz" - integrity sha512-Itrbx6SLUzsZ6Mz3VuOlxhbfuyLTogG5DwEF1V8dAi24iMuvQPIHd7Ti+pNDp7j6WixndJGZaoNR0f9VSzwuTg== - dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" +"@csstools/postcss-gamut-mapping@^2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.11.tgz#be0e34c9f0142852cccfc02b917511f0d677db8b" + integrity sha512-fCpCUgZNE2piVJKC76zFsgVW1apF6dpYsqGyH8SIeCcM4pTEsRTWTLCaJIMKFEundsCKwY1rwfhtrio04RJ4Dw== + dependencies: + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + +"@csstools/postcss-gradients-interpolation-method@^5.0.12": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.12.tgz#0955cce4d97203b861bf66742bbec611b2f3661c" + integrity sha512-jugzjwkUY0wtNrZlFeyXzimUL3hN4xMvoPnIXxoZqxDvjZRiSh+itgHcVUWzJ2VwD/VAMEgCLvtaJHX+4Vj3Ow== + dependencies: + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-hwb-function@^4.0.6": - version "4.0.6" - resolved "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.6.tgz" - integrity sha512-927Pqy3a1uBP7U8sTfaNdZVB0mNXzIrJO/GZ8us9219q9n06gOqCdfZ0E6d1P66Fm0fYHvxfDbfcUuwAn5UwhQ== +"@csstools/postcss-hwb-function@^4.0.12": + version "4.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.12.tgz#07f7ecb08c50e094673bd20eaf7757db0162beee" + integrity sha512-mL/+88Z53KrE4JdePYFJAQWFrcADEqsLprExCM04GDNgHIztwFzj0Mbhd/yxMBngq0NIlz58VVxjt5abNs1VhA== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-ic-unit@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.0.tgz" - integrity sha512-9QT5TDGgx7wD3EEMN3BSUG6ckb6Eh5gSPT5kZoVtUuAonfPmLDJyPhqR4ntPpMYhUKAMVKAg3I/AgzqHMSeLhA== +"@csstools/postcss-ic-unit@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.4.tgz#2ee2da0690db7edfbc469279711b9e69495659d2" + integrity sha512-yQ4VmossuOAql65sCPppVO1yfb7hDscf4GseF0VCA/DTDaBc0Wtf8MTqVPfjGYlT5+2buokG0Gp7y0atYZpwjg== dependencies: - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -"@csstools/postcss-initial@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.0.tgz" - integrity sha512-dv2lNUKR+JV+OOhZm9paWzYBXOCi+rJPqJ2cJuhh9xd8USVrd0cBEPczla81HNOyThMQWeCcdln3gZkQV2kYxA== +"@csstools/postcss-initial@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz#c385bd9d8ad31ad159edd7992069e97ceea4d09a" + integrity sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg== -"@csstools/postcss-is-pseudo-class@^5.0.1": - version "5.0.1" - resolved "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.1.tgz" - integrity sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ== +"@csstools/postcss-is-pseudo-class@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz#d34e850bcad4013c2ed7abe948bfa0448aa8eb74" + integrity sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" -"@csstools/postcss-light-dark-function@^2.0.7": - version "2.0.7" - resolved "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.7.tgz" - integrity sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw== +"@csstools/postcss-light-dark-function@^2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.11.tgz#0df448aab9a33cb9a085264ff1f396fb80c4437d" + integrity sha512-fNJcKXJdPM3Lyrbmgw2OBbaioU7yuKZtiXClf4sGdQttitijYlZMD5K7HrC/eF83VRWRrYq6OZ0Lx92leV2LFA== dependencies: - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" "@csstools/postcss-logical-float-and-clear@^3.0.0": @@ -2336,32 +2408,32 @@ dependencies: postcss-value-parser "^4.2.0" -"@csstools/postcss-logical-viewport-units@^3.0.3": - version "3.0.3" - resolved "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.3.tgz" - integrity sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw== +"@csstools/postcss-logical-viewport-units@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz#016d98a8b7b5f969e58eb8413447eb801add16fc" + integrity sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ== dependencies: - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-media-minmax@^2.0.5": - version "2.0.5" - resolved "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.5.tgz" - integrity sha512-sdh5i5GToZOIAiwhdntRWv77QDtsxP2r2gXW/WbLSCoLr00KTq/yiF1qlQ5XX2+lmiFa8rATKMcbwl3oXDMNew== +"@csstools/postcss-media-minmax@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz#184252d5b93155ae526689328af6bdf3fc113987" + integrity sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/media-query-list-parser" "^4.0.2" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/media-query-list-parser" "^4.0.3" -"@csstools/postcss-media-queries-aspect-ratio-number-values@^3.0.4": - version "3.0.4" - resolved "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.4.tgz" - integrity sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ== +"@csstools/postcss-media-queries-aspect-ratio-number-values@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz#f485c31ec13d6b0fb5c528a3474334a40eff5f11" + integrity sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg== dependencies: - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/media-query-list-parser" "^4.0.2" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/media-query-list-parser" "^4.0.3" "@csstools/postcss-nested-calc@^4.0.0": version "4.0.0" @@ -2378,42 +2450,47 @@ dependencies: postcss-value-parser "^4.2.0" -"@csstools/postcss-oklab-function@^4.0.6": - version "4.0.6" - resolved "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.6.tgz" - integrity sha512-Hptoa0uX+XsNacFBCIQKTUBrFKDiplHan42X73EklG6XmQLG7/aIvxoNhvZ7PvOWMt67Pw3bIlUY2nD6p5vL8A== +"@csstools/postcss-oklab-function@^4.0.12": + version "4.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.12.tgz#416640ef10227eea1375b47b72d141495950971d" + integrity sha512-HhlSmnE1NKBhXsTnNGjxvhryKtO7tJd1w42DKOGFD6jSHtYOrsJTQDKPMwvOfrzUAk8t7GcpIfRyM7ssqHpFjg== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -"@csstools/postcss-progressive-custom-properties@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz" - integrity sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q== - dependencies: - postcss-value-parser "^4.2.0" +"@csstools/postcss-position-area-property@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-position-area-property/-/postcss-position-area-property-1.0.0.tgz#41f0cbc737a81a42890d5ec035fa26a45f4f4ad4" + integrity sha512-fUP6KR8qV2NuUZV3Cw8itx0Ep90aRjAZxAEzC3vrl6yjFv+pFsQbR18UuQctEKmA72K9O27CoYiKEgXxkqjg8Q== -"@csstools/postcss-random-function@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-1.0.1.tgz" - integrity sha512-Ab/tF8/RXktQlFwVhiC70UNfpFQRhtE5fQQoP2pO+KCPGLsLdWFiOuHgSRtBOqEshCVAzR4H6o38nhvRZq8deA== +"@csstools/postcss-progressive-custom-properties@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz#c39780b9ff0d554efb842b6bd75276aa6f1705db" + integrity sha512-uPiiXf7IEKtUQXsxu6uWtOlRMXd2QWWy5fhxHDnPdXKCQckPP3E34ZgDoZ62r2iT+UOgWsSbM4NvHE5m3mAEdw== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + postcss-value-parser "^4.2.0" -"@csstools/postcss-relative-color-syntax@^3.0.6": - version "3.0.6" - resolved "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.6.tgz" - integrity sha512-yxP618Xb+ji1I624jILaYM62uEmZcmbdmFoZHoaThw896sq0vU39kqTTF+ZNic9XyPtPMvq0vyvbgmHaszq8xg== - dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" +"@csstools/postcss-random-function@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz#3191f32fe72936e361dadf7dbfb55a0209e2691e" + integrity sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w== + dependencies: + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + +"@csstools/postcss-relative-color-syntax@^3.0.12": + version "3.0.12" + resolved "https://registry.yarnpkg.com/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.12.tgz#ced792450102441f7c160e1d106f33e4b44181f8" + integrity sha512-0RLIeONxu/mtxRtf3o41Lq2ghLimw0w9ByLWnnEVuy89exmEEq8bynveBxNW3nyHqLAFEeNtVEmC1QK9MZ8Huw== + dependencies: + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" "@csstools/postcss-scope-pseudo-class@^4.0.1": @@ -2423,50 +2500,58 @@ dependencies: postcss-selector-parser "^7.0.0" -"@csstools/postcss-sign-functions@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.0.tgz" - integrity sha512-SLcc20Nujx/kqbSwDmj6oaXgpy3UjFhBy1sfcqPgDkHfOIfUtUVH7OXO+j7BU4v/At5s61N5ZX6shvgPwluhsA== +"@csstools/postcss-sign-functions@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz#a9ac56954014ae4c513475b3f1b3e3424a1e0c12" + integrity sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" -"@csstools/postcss-stepped-value-functions@^4.0.5": - version "4.0.5" - resolved "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.5.tgz" - integrity sha512-G6SJ6hZJkhxo6UZojVlLo14MohH4J5J7z8CRBrxxUYy9JuZiIqUo5TBYyDGcE0PLdzpg63a7mHSJz3VD+gMwqw== +"@csstools/postcss-stepped-value-functions@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz#36036f1a0e5e5ee2308e72f3c9cb433567c387b9" + integrity sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" -"@csstools/postcss-text-decoration-shorthand@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.1.tgz" - integrity sha512-xPZIikbx6jyzWvhms27uugIc0I4ykH4keRvoa3rxX5K7lEhkbd54rjj/dv60qOCTisoS+3bmwJTeyV1VNBrXaw== +"@csstools/postcss-system-ui-font-family@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@csstools/postcss-system-ui-font-family/-/postcss-system-ui-font-family-1.0.0.tgz#bd65b79078debf6f67b318dc9b71a8f9fa16f8c8" + integrity sha512-s3xdBvfWYfoPSBsikDXbuorcMG1nN1M6GdU0qBsGfcmNR0A/qhloQZpTxjA3Xsyrk1VJvwb2pOfiOT3at/DuIQ== dependencies: - "@csstools/color-helpers" "^5.0.1" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + +"@csstools/postcss-text-decoration-shorthand@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz#fae1b70f07d1b7beb4c841c86d69e41ecc6f743c" + integrity sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA== + dependencies: + "@csstools/color-helpers" "^5.1.0" postcss-value-parser "^4.2.0" -"@csstools/postcss-trigonometric-functions@^4.0.5": - version "4.0.5" - resolved "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.5.tgz" - integrity sha512-/YQThYkt5MLvAmVu7zxjhceCYlKrYddK6LEmK5I4ojlS6BmO9u2yO4+xjXzu2+NPYmHSTtP4NFSamBCMmJ1NJA== +"@csstools/postcss-trigonometric-functions@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz#3f94ed2e319b57f2c59720b64e4d0a8a6fb8c3b2" + integrity sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A== dependencies: - "@csstools/css-calc" "^2.1.0" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/css-calc" "^2.1.4" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/postcss-unset-value@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz" integrity sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA== -"@csstools/selector-resolve-nested@^3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz" - integrity sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ== +"@csstools/selector-resolve-nested@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz#848c6f44cb65e3733e478319b9342b7aa436fac7" + integrity sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g== "@csstools/selector-specificity@^5.0.0": version "5.0.0" @@ -2483,25 +2568,34 @@ resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@docsearch/css@3.6.0": - version "3.6.0" - resolved "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz" - integrity sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ== +"@docsearch/core@4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@docsearch/core/-/core-4.3.1.tgz#88a97a6fe4d4025269b6dee8b9d070b76758ad82" + integrity sha512-ktVbkePE+2h9RwqCUMbWXOoebFyDOxHqImAqfs+lC8yOU+XwEW4jgvHGJK079deTeHtdhUNj0PXHSnhJINvHzQ== -"@docsearch/react@^3.5.2": - version "3.6.0" - resolved "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz" - integrity sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w== - dependencies: - "@algolia/autocomplete-core" "1.9.3" - "@algolia/autocomplete-preset-algolia" "1.9.3" - "@docsearch/css" "3.6.0" - algoliasearch "^4.19.1" +"@docsearch/css@4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-4.3.2.tgz#d47d25336c9516b419245fa74e8dd5ae84a17492" + integrity sha512-K3Yhay9MgkBjJJ0WEL5MxnACModX9xuNt3UlQQkDEDZJZ0+aeWKtOkxHNndMRkMBnHdYvQjxkm6mdlneOtU1IQ== -"@docusaurus/babel@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.6.3.tgz" - integrity sha512-7dW9Hat9EHYCVicFXYA4hjxBY38+hPuCURL8oRF9fySRm7vzNWuEOghA1TXcykuXZp0HLG2td4RhDxCvGG7tNw== +"@docsearch/react@^3.9.0 || ^4.1.0": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-4.3.2.tgz#450b8341cb5cca03737a00075d4dfd3a904a3e3e" + integrity sha512-74SFD6WluwvgsOPqifYOviEEVwDxslxfhakTlra+JviaNcs7KK/rjsPj89kVEoQc9FUxRkAofaJnHIR7pb4TSQ== + dependencies: + "@ai-sdk/react" "^2.0.30" + "@algolia/autocomplete-core" "1.19.2" + "@docsearch/core" "4.3.1" + "@docsearch/css" "4.3.2" + ai "^5.0.30" + algoliasearch "^5.28.0" + marked "^16.3.0" + zod "^4.1.8" + +"@docusaurus/babel@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.9.2.tgz#f956c638baeccf2040e482c71a742bc7e35fdb22" + integrity sha512-GEANdi/SgER+L7Japs25YiGil/AUDnFFHaCGPBbundxoWtCkA2lmy7/tFmgED4y1htAy6Oi4wkJEQdGssnw9MA== dependencies: "@babel/core" "^7.25.9" "@babel/generator" "^7.25.9" @@ -2513,55 +2607,54 @@ "@babel/runtime" "^7.25.9" "@babel/runtime-corejs3" "^7.25.9" "@babel/traverse" "^7.25.9" - "@docusaurus/logger" "3.6.3" - "@docusaurus/utils" "3.6.3" + "@docusaurus/logger" "3.9.2" + "@docusaurus/utils" "3.9.2" babel-plugin-dynamic-import-node "^2.3.3" fs-extra "^11.1.1" tslib "^2.6.0" -"@docusaurus/bundler@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.6.3.tgz" - integrity sha512-47JLuc8D4wA+6VOvmMd5fUC9rFppBQpQOnxDYiVXffm/DeV/wmm3sbpNd5Y+O+G2+nevLTRnvCm/qyancv0Y3A== +"@docusaurus/bundler@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.9.2.tgz#0ca82cda4acf13a493e3f66061aea351e9d356cf" + integrity sha512-ZOVi6GYgTcsZcUzjblpzk3wH1Fya2VNpd5jtHoCCFcJlMQ1EYXZetfAnRHLcyiFeBABaI1ltTYbOBtH/gahGVA== dependencies: "@babel/core" "^7.25.9" - "@docusaurus/babel" "3.6.3" - "@docusaurus/cssnano-preset" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" + "@docusaurus/babel" "3.9.2" + "@docusaurus/cssnano-preset" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" babel-loader "^9.2.1" - clean-css "^5.3.2" + clean-css "^5.3.3" copy-webpack-plugin "^11.0.0" - css-loader "^6.8.1" + css-loader "^6.11.0" css-minimizer-webpack-plugin "^5.0.1" cssnano "^6.1.2" file-loader "^6.2.0" html-minifier-terser "^7.2.0" - mini-css-extract-plugin "^2.9.1" + mini-css-extract-plugin "^2.9.2" null-loader "^4.0.1" - postcss "^8.4.26" - postcss-loader "^7.3.3" - postcss-preset-env "^10.1.0" - react-dev-utils "^12.0.1" + postcss "^8.5.4" + postcss-loader "^7.3.4" + postcss-preset-env "^10.2.1" terser-webpack-plugin "^5.3.9" tslib "^2.6.0" url-loader "^4.1.1" webpack "^5.95.0" webpackbar "^6.0.1" -"@docusaurus/core@3.6.3", "@docusaurus/core@^3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/core/-/core-3.6.3.tgz" - integrity sha512-xL7FRY9Jr5DWqB6pEnqgKqcMPJOX5V0pgWXi5lCiih11sUBmcFKM7c3+GyxcVeeWFxyYSDP3grLTWqJoP4P9Vw== - dependencies: - "@docusaurus/babel" "3.6.3" - "@docusaurus/bundler" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" +"@docusaurus/core@3.9.2", "@docusaurus/core@^3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.9.2.tgz#cc970f29b85a8926d63c84f8cffdcda43ed266ff" + integrity sha512-HbjwKeC+pHUFBfLMNzuSjqFE/58+rLVKmOU3lxQrpsxLBOGosYco/Q0GduBb0/jEMRiyEqjNT/01rRdOMWq5pw== + dependencies: + "@docusaurus/babel" "3.9.2" + "@docusaurus/bundler" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" boxen "^6.2.1" chalk "^4.1.2" chokidar "^3.5.3" @@ -2569,100 +2662,61 @@ combine-promises "^1.1.0" commander "^5.1.0" core-js "^3.31.1" - del "^6.1.1" detect-port "^1.5.1" escape-html "^1.0.3" eta "^2.2.0" eval "^0.1.8" + execa "5.1.1" fs-extra "^11.1.1" html-tags "^3.3.1" html-webpack-plugin "^5.6.0" leven "^3.1.0" lodash "^4.17.21" + open "^8.4.0" p-map "^4.0.0" prompts "^2.4.2" - react-dev-utils "^12.0.1" - react-helmet-async "^1.3.0" + react-helmet-async "npm:@slorber/react-helmet-async@1.3.0" react-loadable "npm:@docusaurus/react-loadable@6.0.0" react-loadable-ssr-addon-v5-slorber "^1.0.1" react-router "^5.3.4" react-router-config "^5.1.1" react-router-dom "^5.3.4" - rtl-detect "^1.0.4" semver "^7.5.4" serve-handler "^6.1.6" - shelljs "^0.8.5" + tinypool "^1.0.2" tslib "^2.6.0" update-notifier "^6.0.2" webpack "^5.95.0" webpack-bundle-analyzer "^4.10.2" - webpack-dev-server "^4.15.2" + webpack-dev-server "^5.2.2" webpack-merge "^6.0.1" -"@docusaurus/cssnano-preset@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.3.tgz" - integrity sha512-qP7SXrwZ+23GFJdPN4aIHQrZW+oH/7tzwEuc/RNL0+BdZdmIjYQqUxdXsjE4lFxLNZjj0eUrSNYIS6xwfij+5Q== +"@docusaurus/cssnano-preset@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.9.2.tgz#523aab65349db3c51a77f2489048d28527759428" + integrity sha512-8gBKup94aGttRduABsj7bpPFTX7kbwu+xh3K9NMCF5K4bWBqTFYW+REKHF6iBVDHRJ4grZdIPbvkiHd/XNKRMQ== dependencies: cssnano-preset-advanced "^6.1.2" - postcss "^8.4.38" + postcss "^8.5.4" postcss-sort-media-queries "^5.2.0" tslib "^2.6.0" -"@docusaurus/logger@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.6.3.tgz" - integrity sha512-xSubJixcNyMV9wMV4q0s47CBz3Rlc5jbcCCuij8pfQP8qn/DIpt0ks8W6hQWzHAedg/J/EwxxUOUrnEoKzJo8g== - dependencies: - chalk "^4.1.2" - tslib "^2.6.0" - -"@docusaurus/logger@3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.8.1.tgz#45321b2e2e14695d0dbd8b4104ea7b0fbaa98700" - integrity sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww== +"@docusaurus/logger@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.9.2.tgz#6ec6364b90f5a618a438cc9fd01ac7376869f92a" + integrity sha512-/SVCc57ByARzGSU60c50rMyQlBuMIJCjcsJlkphxY6B0GV4UH3tcA1994N8fFfbJ9kX3jIBe/xg3XP5qBtGDbA== dependencies: chalk "^4.1.2" tslib "^2.6.0" -"@docusaurus/mdx-loader@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.6.3.tgz" - integrity sha512-3iJdiDz9540ppBseeI93tWTDtUGVkxzh59nMq4ignylxMuXBLK8dFqVeaEor23v1vx6TrGKZ2FuLaTB+U7C0QQ== +"@docusaurus/mdx-loader@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.9.2.tgz#78d238de6c6203fa811cc2a7e90b9b79e111408c" + integrity sha512-wiYoGwF9gdd6rev62xDU8AAM8JuLI/hlwOtCzMmYcspEkzecKrP8J8X+KpYnTlACBUUtXNJpSoCwFWJhLRevzQ== dependencies: - "@docusaurus/logger" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" - "@mdx-js/mdx" "^3.0.0" - "@slorber/remark-comment" "^1.0.0" - escape-html "^1.0.3" - estree-util-value-to-estree "^3.0.1" - file-loader "^6.2.0" - fs-extra "^11.1.1" - image-size "^1.0.2" - mdast-util-mdx "^3.0.0" - mdast-util-to-string "^4.0.0" - rehype-raw "^7.0.0" - remark-directive "^3.0.0" - remark-emoji "^4.0.0" - remark-frontmatter "^5.0.0" - remark-gfm "^4.0.0" - stringify-object "^3.3.0" - tslib "^2.6.0" - unified "^11.0.3" - unist-util-visit "^5.0.0" - url-loader "^4.1.1" - vfile "^6.0.1" - webpack "^5.88.1" - -"@docusaurus/mdx-loader@3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.8.1.tgz#74309b3614bbcef1d55fb13e6cc339b7fb000b5f" - integrity sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w== - dependencies: - "@docusaurus/logger" "3.8.1" - "@docusaurus/utils" "3.8.1" - "@docusaurus/utils-validation" "3.8.1" + "@docusaurus/logger" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@mdx-js/mdx" "^3.0.0" "@slorber/remark-comment" "^1.0.0" escape-html "^1.0.3" @@ -2685,25 +2739,12 @@ vfile "^6.0.1" webpack "^5.88.1" -"@docusaurus/module-type-aliases@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.3.tgz" - integrity sha512-MjaXX9PN/k5ugNvfRZdWyKWq4FsrhN4LEXaj0pEmMebJuBNlFeGyKQUa9DRhJHpadNaiMLrbo9m3U7Ig5YlsZg== - dependencies: - "@docusaurus/types" "3.6.3" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - "@types/react-router-dom" "*" - react-helmet-async "*" - react-loadable "npm:@docusaurus/react-loadable@6.0.0" - -"@docusaurus/module-type-aliases@3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.1.tgz#454de577bd7f50b5eae16db0f76b49ca5e4e281a" - integrity sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg== +"@docusaurus/module-type-aliases@3.9.2", "@docusaurus/module-type-aliases@^3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.9.2.tgz#993c7cb0114363dea5ef6855e989b3ad4b843a34" + integrity sha512-8qVe2QA9hVLzvnxP46ysuofJUIc/yYQ82tvA/rBTrnpXtCjNSFLxEZfd5U8cYZuJIVlkPxamsIgwd5tGZXfvew== dependencies: - "@docusaurus/types" "3.8.1" + "@docusaurus/types" "3.9.2" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -2711,182 +2752,196 @@ react-helmet-async "npm:@slorber/react-helmet-async@1.3.0" react-loadable "npm:@docusaurus/react-loadable@6.0.0" -"@docusaurus/module-type-aliases@^3.3.2": - version "3.3.2" - resolved "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.3.2.tgz" - integrity sha512-b/XB0TBJah5yKb4LYuJT4buFvL0MGAb0+vJDrJtlYMguRtsEBkf2nWl5xP7h4Dlw6ol0hsHrCYzJ50kNIOEclw== - dependencies: - "@docusaurus/types" "3.3.2" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - "@types/react-router-dom" "*" - react-helmet-async "*" - react-loadable "npm:@docusaurus/react-loadable@6.0.0" - -"@docusaurus/plugin-content-blog@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.3.tgz" - integrity sha512-k0ogWwwJU3pFRFfvW1kRVHxzf2DutLGaaLjAnHVEU6ju+aRP0Z5ap/13DHyPOfHeE4WKpn/M0TqjdwZAcY3kAw== - dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" +"@docusaurus/plugin-content-blog@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.9.2.tgz#d5ce51eb7757bdab0515e2dd26a793ed4e119df9" + integrity sha512-3I2HXy3L1QcjLJLGAoTvoBnpOwa6DPUa3Q0dMK19UTY9mhPkKQg/DYhAGTiBUKcTR0f08iw7kLPqOhIgdV3eVQ== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" cheerio "1.0.0-rc.12" feed "^4.2.2" fs-extra "^11.1.1" lodash "^4.17.21" - reading-time "^1.5.0" + schema-dts "^1.1.2" srcset "^4.0.0" tslib "^2.6.0" unist-util-visit "^5.0.0" utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-docs@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.3.tgz" - integrity sha512-r2wS8y/fsaDcxkm20W5bbYJFPzdWdEaTWVYjNxlHlcmX086eqQR1Fomlg9BHTJ0dLXPzAlbC8EN4XqMr3QzNCQ== - dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/module-type-aliases" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" +"@docusaurus/plugin-content-docs@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz#cd8f2d1c06e53c3fa3d24bdfcb48d237bf2d6b2e" + integrity sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@types/react-router-config" "^5.0.7" combine-promises "^1.1.0" fs-extra "^11.1.1" js-yaml "^4.1.0" lodash "^4.17.21" + schema-dts "^1.1.2" tslib "^2.6.0" utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-pages@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.3.tgz" - integrity sha512-eHrmTgjgLZsuqfsYr5X2xEwyIcck0wseSofWrjTwT9FLOWp+KDmMAuVK+wRo7sFImWXZk3oV/xX/g9aZrhD7OA== +"@docusaurus/plugin-content-pages@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.9.2.tgz#22db6c88ade91cec0a9e87a00b8089898051b08d" + integrity sha512-s4849w/p4noXUrGpPUF0BPqIAfdAe76BLaRGAGKZ1gTDNiGxGcpsLcwJ9OTi1/V8A+AzvsmI9pkjie2zjIQZKA== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" fs-extra "^11.1.1" tslib "^2.6.0" webpack "^5.88.1" -"@docusaurus/plugin-debug@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.6.3.tgz" - integrity sha512-zB9GXfIZNPRfzKnNjU6xGVrqn9bPXuGhpjgsuc/YtcTDjnjhasg38NdYd5LEqXex5G/zIorQgWB3n6x/Ut62vQ== +"@docusaurus/plugin-css-cascade-layers@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.9.2.tgz#358c85f63f1c6a11f611f1b8889d9435c11b22f8" + integrity sha512-w1s3+Ss+eOQbscGM4cfIFBlVg/QKxyYgj26k5AnakuHkKxH6004ZtuLe5awMBotIYF2bbGDoDhpgQ4r/kcj4rQ== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + tslib "^2.6.0" + +"@docusaurus/plugin-debug@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.9.2.tgz#b5df4db115583f5404a252dbf66f379ff933e53c" + integrity sha512-j7a5hWuAFxyQAkilZwhsQ/b3T7FfHZ+0dub6j/GxKNFJp2h9qk/P1Bp7vrGASnvA9KNQBBL1ZXTe7jlh4VdPdA== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" fs-extra "^11.1.1" - react-json-view-lite "^1.2.0" + react-json-view-lite "^2.3.0" tslib "^2.6.0" -"@docusaurus/plugin-google-analytics@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.3.tgz" - integrity sha512-rCDNy1QW8Dag7nZq67pcum0bpFLrwvxJhYuVprhFh8BMBDxV0bY+bAkGHbSf68P3Bk9C3hNOAXX1srGLIDvcTA== +"@docusaurus/plugin-google-analytics@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.9.2.tgz#857fe075fdeccdf6959e62954d9efe39769fa247" + integrity sha512-mAwwQJ1Us9jL/lVjXtErXto4p4/iaLlweC54yDUK1a97WfkC6Z2k5/769JsFgwOwOP+n5mUQGACXOEQ0XDuVUw== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" tslib "^2.6.0" -"@docusaurus/plugin-google-gtag@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.3.tgz" - integrity sha512-+OyDvhM6rqVkQOmLVkQWVJAizEEfkPzVWtIHXlWPOCFGK9X4/AWeBSrU0WG4iMg9Z4zD4YDRrU+lvI4s6DSC+w== +"@docusaurus/plugin-google-gtag@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.9.2.tgz#df75b1a90ae9266b0471909ba0265f46d5dcae62" + integrity sha512-YJ4lDCphabBtw19ooSlc1MnxtYGpjFV9rEdzjLsUnBCeis2djUyCozZaFhCg6NGEwOn7HDDyMh0yzcdRpnuIvA== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@types/gtag.js" "^0.0.12" tslib "^2.6.0" -"@docusaurus/plugin-google-tag-manager@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.3.tgz" - integrity sha512-1M6UPB13gWUtN2UHX083/beTn85PlRI9ABItTl/JL1FJ5dJTWWFXXsHf9WW/6hrVwthwTeV/AGbGKvLKV+IlCA== +"@docusaurus/plugin-google-tag-manager@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.9.2.tgz#d1a3cf935acb7d31b84685e92d70a1d342946677" + integrity sha512-LJtIrkZN/tuHD8NqDAW1Tnw0ekOwRTfobWPsdO15YxcicBo2ykKF0/D6n0vVBfd3srwr9Z6rzrIWYrMzBGrvNw== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" tslib "^2.6.0" -"@docusaurus/plugin-sitemap@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.3.tgz" - integrity sha512-94qOO4M9Fwv9KfVQJsgbe91k+fPJ4byf1L3Ez8TUa6TAFPo/BrLwQ80zclHkENlL1824TuxkcMKv33u6eydQCg== - dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" +"@docusaurus/plugin-sitemap@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.9.2.tgz#e1d9f7012942562cc0c6543d3cb2cdc4ae713dc4" + integrity sha512-WLh7ymgDXjG8oPoM/T4/zUP7KcSuFYRZAUTl8vR6VzYkfc18GBM4xLhcT+AKOwun6kBivYKUJf+vlqYJkm+RHw== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" fs-extra "^11.1.1" sitemap "^7.1.1" tslib "^2.6.0" -"@docusaurus/preset-classic@^3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.6.3.tgz" - integrity sha512-VHSYWROT3flvNNI1SrnMOtW1EsjeHNK9dhU6s9eY5hryZe79lUqnZJyze/ymDe2LXAqzyj6y5oYvyBoZZk6ErA== - dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/plugin-content-blog" "3.6.3" - "@docusaurus/plugin-content-docs" "3.6.3" - "@docusaurus/plugin-content-pages" "3.6.3" - "@docusaurus/plugin-debug" "3.6.3" - "@docusaurus/plugin-google-analytics" "3.6.3" - "@docusaurus/plugin-google-gtag" "3.6.3" - "@docusaurus/plugin-google-tag-manager" "3.6.3" - "@docusaurus/plugin-sitemap" "3.6.3" - "@docusaurus/theme-classic" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/theme-search-algolia" "3.6.3" - "@docusaurus/types" "3.6.3" - -"@docusaurus/theme-classic@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.6.3.tgz" - integrity sha512-1RRLK1tSArI2c00qugWYO3jRocjOZwGF1mBzPPylDVRwWCS/rnWWR91ChdbbaxIupRJ+hX8ZBYrwr5bbU0oztQ== - dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/module-type-aliases" "3.6.3" - "@docusaurus/plugin-content-blog" "3.6.3" - "@docusaurus/plugin-content-docs" "3.6.3" - "@docusaurus/plugin-content-pages" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/theme-translations" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" +"@docusaurus/plugin-svgr@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-svgr/-/plugin-svgr-3.9.2.tgz#62857ed79d97c0150d25f7e7380fdee65671163a" + integrity sha512-n+1DE+5b3Lnf27TgVU5jM1d4x5tUh2oW5LTsBxJX4PsAPV0JGcmI6p3yLYtEY0LRVEIJh+8RsdQmRE66wSV8mw== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + "@svgr/core" "8.1.0" + "@svgr/webpack" "^8.1.0" + tslib "^2.6.0" + webpack "^5.88.1" + +"@docusaurus/preset-classic@^3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.9.2.tgz#85cc4f91baf177f8146c9ce896dfa1f0fd377050" + integrity sha512-IgyYO2Gvaigi21LuDIe+nvmN/dfGXAiMcV/murFqcpjnZc7jxFAxW+9LEjdPt61uZLxG4ByW/oUmX/DDK9t/8w== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/plugin-content-blog" "3.9.2" + "@docusaurus/plugin-content-docs" "3.9.2" + "@docusaurus/plugin-content-pages" "3.9.2" + "@docusaurus/plugin-css-cascade-layers" "3.9.2" + "@docusaurus/plugin-debug" "3.9.2" + "@docusaurus/plugin-google-analytics" "3.9.2" + "@docusaurus/plugin-google-gtag" "3.9.2" + "@docusaurus/plugin-google-tag-manager" "3.9.2" + "@docusaurus/plugin-sitemap" "3.9.2" + "@docusaurus/plugin-svgr" "3.9.2" + "@docusaurus/theme-classic" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/theme-search-algolia" "3.9.2" + "@docusaurus/types" "3.9.2" + +"@docusaurus/theme-classic@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.9.2.tgz#6e514f99a0ff42b80afcf42d5e5d042618311ce0" + integrity sha512-IGUsArG5hhekXd7RDb11v94ycpJpFdJPkLnt10fFQWOVxAtq5/D7hT6lzc2fhyQKaaCE62qVajOMKL7OiAFAIA== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/plugin-content-blog" "3.9.2" + "@docusaurus/plugin-content-docs" "3.9.2" + "@docusaurus/plugin-content-pages" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/theme-translations" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@mdx-js/react" "^3.0.0" clsx "^2.0.0" - copy-text-to-clipboard "^3.2.0" infima "0.2.0-alpha.45" lodash "^4.17.21" nprogress "^0.2.0" - postcss "^8.4.26" + postcss "^8.5.4" prism-react-renderer "^2.3.0" prismjs "^1.29.0" react-router-dom "^5.3.4" @@ -2894,33 +2949,15 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-common@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.6.3.tgz" - integrity sha512-b8ZkhczXHDxWWyvz+YJy4t/PlPbEogTTbgnHoflYnH7rmRtyoodTsu8WVM12la5LmlMJBclBXFl29OH8kPE7gg== - dependencies: - "@docusaurus/mdx-loader" "3.6.3" - "@docusaurus/module-type-aliases" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - clsx "^2.0.0" - parse-numeric-range "^1.3.0" - prism-react-renderer "^2.3.0" - tslib "^2.6.0" - utility-types "^3.10.0" - -"@docusaurus/theme-common@^3.6.3": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.8.1.tgz#17c23316fbe3ee3f7e707c7298cb59a0fff38b4b" - integrity sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw== +"@docusaurus/theme-common@3.9.2", "@docusaurus/theme-common@^3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.9.2.tgz#487172c6fef9815c2746ef62a71e4f5b326f9ba5" + integrity sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag== dependencies: - "@docusaurus/mdx-loader" "3.8.1" - "@docusaurus/module-type-aliases" "3.8.1" - "@docusaurus/utils" "3.8.1" - "@docusaurus/utils-common" "3.8.1" + "@docusaurus/mdx-loader" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -2930,49 +2967,49 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-live-codeblock@^3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.6.3.tgz" - integrity sha512-l6T+rpfiG5FT3kSWEtW7no2VMU7J0hqwNOXC2iubfzkQCLB+A5asP7kGTzBxSn6OKKsj9FY7p26A6CVvy4dvWQ== +"@docusaurus/theme-live-codeblock@^3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.9.2.tgz#43f0968fb737fda1dae2222a2ab7caa25c5668d0" + integrity sha512-cgxxZh18dI5Q4iV0GLmwqXtgZbTLOnb0TYgZRiUh0mnIGbuNWFUhUYXXl5owKbDfIXFdFAiI/owJKM83howEAw== dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/theme-translations" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" + "@docusaurus/core" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/theme-translations" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" "@philpl/buble" "^0.19.7" clsx "^2.0.0" fs-extra "^11.1.1" react-live "^4.1.6" tslib "^2.6.0" -"@docusaurus/theme-mermaid@^3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.6.3.tgz" - integrity sha512-kIqpjNCP/9R2GGf8UmiDxD3CkOAEJuJIEFlaKMgQtjVxa/vH+9PLI1+DFbArGoG4+0ENTYUq8phHPW7SeL36uQ== - dependencies: - "@docusaurus/core" "3.6.3" - "@docusaurus/module-type-aliases" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" - mermaid ">=10.4" +"@docusaurus/theme-mermaid@^3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-mermaid/-/theme-mermaid-3.9.2.tgz#f065e4b4b319560ddd8c3be65ce9dd19ce1d5cc8" + integrity sha512-5vhShRDq/ntLzdInsQkTdoKWSzw8d1jB17sNPYhA/KvYYFXfuVEGHLM6nrf8MFbV8TruAHDG21Fn3W4lO8GaDw== + dependencies: + "@docusaurus/core" "3.9.2" + "@docusaurus/module-type-aliases" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + mermaid ">=11.6.0" tslib "^2.6.0" -"@docusaurus/theme-search-algolia@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.3.tgz" - integrity sha512-rt+MGCCpYgPyWCGXtbxlwFbTSobu15jWBTPI2LHsHNa5B0zSmOISX6FWYAPt5X1rNDOqMGM0FATnh7TBHRohVA== - dependencies: - "@docsearch/react" "^3.5.2" - "@docusaurus/core" "3.6.3" - "@docusaurus/logger" "3.6.3" - "@docusaurus/plugin-content-docs" "3.6.3" - "@docusaurus/theme-common" "3.6.3" - "@docusaurus/theme-translations" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-validation" "3.6.3" - algoliasearch "^4.18.0" - algoliasearch-helper "^3.13.3" +"@docusaurus/theme-search-algolia@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.9.2.tgz#420fd5b27fc1673b48151fdc9fe7167ba135ed50" + integrity sha512-GBDSFNwjnh5/LdkxCKQHkgO2pIMX1447BxYUBG2wBiajS21uj64a+gH/qlbQjDLxmGrbrllBrtJkUHxIsiwRnw== + dependencies: + "@docsearch/react" "^3.9.0 || ^4.1.0" + "@docusaurus/core" "3.9.2" + "@docusaurus/logger" "3.9.2" + "@docusaurus/plugin-content-docs" "3.9.2" + "@docusaurus/theme-common" "3.9.2" + "@docusaurus/theme-translations" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-validation" "3.9.2" + algoliasearch "^5.37.0" + algoliasearch-helper "^3.26.0" clsx "^2.0.0" eta "^2.2.0" fs-extra "^11.1.1" @@ -2980,51 +3017,22 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.6.3.tgz" - integrity sha512-Gb0regclToVlngSIIwUCtBMQBq48qVUaN1XQNKW4XwlsgUyk0vP01LULdqbem7czSwIeBAFXFoORJ0RPX7ht/w== +"@docusaurus/theme-translations@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.9.2.tgz#238cd69c2da92d612be3d3b4f95944c1d0f1e041" + integrity sha512-vIryvpP18ON9T9rjgMRFLr2xJVDpw1rtagEGf8Ccce4CkTrvM/fRB8N2nyWYOW5u3DdjkwKw5fBa+3tbn9P4PA== dependencies: fs-extra "^11.1.1" tslib "^2.6.0" -"@docusaurus/types@3.3.2": - version "3.3.2" - resolved "https://registry.npmjs.org/@docusaurus/types/-/types-3.3.2.tgz" - integrity sha512-5p201S7AZhliRxTU7uMKtSsoC8mgPA9bs9b5NQg1IRdRxJfflursXNVsgc3PcMqiUTul/v1s3k3rXXFlRE890w== - dependencies: - "@mdx-js/mdx" "^3.0.0" - "@types/history" "^4.7.11" - "@types/react" "*" - commander "^5.1.0" - joi "^17.9.2" - react-helmet-async "^1.3.0" - utility-types "^3.10.0" - webpack "^5.88.1" - webpack-merge "^5.9.0" - -"@docusaurus/types@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/types/-/types-3.6.3.tgz" - integrity sha512-xD9oTGDrouWzefkhe9ogB2fDV96/82cRpNGx2HIvI5L87JHNhQVIWimQ/3JIiiX/TEd5S9s+VO6FFguwKNRVow== - dependencies: - "@mdx-js/mdx" "^3.0.0" - "@types/history" "^4.7.11" - "@types/react" "*" - commander "^5.1.0" - joi "^17.9.2" - react-helmet-async "^1.3.0" - utility-types "^3.10.0" - webpack "^5.95.0" - webpack-merge "^5.9.0" - -"@docusaurus/types@3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.8.1.tgz#83ab66c345464e003b576a49f78897482061fc26" - integrity sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg== +"@docusaurus/types@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.9.2.tgz#e482cf18faea0d1fa5ce0e3f1e28e0f32d2593eb" + integrity sha512-Ux1JUNswg+EfUEmajJjyhIohKceitY/yzjRUpu04WXgvVz+fbhVC0p+R0JhvEu4ytw8zIAys2hrdpQPBHRIa8Q== dependencies: "@mdx-js/mdx" "^3.0.0" "@types/history" "^4.7.11" + "@types/mdast" "^4.0.2" "@types/react" "*" commander "^5.1.0" joi "^17.9.2" @@ -3033,85 +3041,36 @@ webpack "^5.95.0" webpack-merge "^5.9.0" -"@docusaurus/utils-common@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.6.3.tgz" - integrity sha512-v4nKDaANLgT3pMBewHYEMAl/ufY0LkXao1QkFWzI5huWFOmNQ2UFzv2BiKeHX5Ownis0/w6cAyoxPhVdDonlSQ== - dependencies: - "@docusaurus/types" "3.6.3" - tslib "^2.6.0" - -"@docusaurus/utils-common@3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.8.1.tgz#c369b8c3041afb7dcd595d4172beb1cc1015c85f" - integrity sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg== +"@docusaurus/utils-common@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.9.2.tgz#e89bfcf43d66359f43df45293fcdf22814847460" + integrity sha512-I53UC1QctruA6SWLvbjbhCpAw7+X7PePoe5pYcwTOEXD/PxeP8LnECAhTHHwWCblyUX5bMi4QLRkxvyZ+IT8Aw== dependencies: - "@docusaurus/types" "3.8.1" + "@docusaurus/types" "3.9.2" tslib "^2.6.0" -"@docusaurus/utils-validation@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.6.3.tgz" - integrity sha512-bhEGGiN5BE38h21vjqD70Gxg++j+PfYVddDUE5UFvLDup68QOcpD33CLr+2knPorlxRbEaNfz6HQDUMQ3HuqKw== +"@docusaurus/utils-validation@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.9.2.tgz#04aec285604790806e2fc5aa90aa950dc7ba75ae" + integrity sha512-l7yk3X5VnNmATbwijJkexdhulNsQaNDwoagiwujXoxFbWLcxHQqNQ+c/IAlzrfMMOfa/8xSBZ7KEKDesE/2J7A== dependencies: - "@docusaurus/logger" "3.6.3" - "@docusaurus/utils" "3.6.3" - "@docusaurus/utils-common" "3.6.3" + "@docusaurus/logger" "3.9.2" + "@docusaurus/utils" "3.9.2" + "@docusaurus/utils-common" "3.9.2" fs-extra "^11.2.0" joi "^17.9.2" js-yaml "^4.1.0" lodash "^4.17.21" tslib "^2.6.0" -"@docusaurus/utils-validation@3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.8.1.tgz#0499c0d151a4098a0963237057993282cfbd538e" - integrity sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA== +"@docusaurus/utils@3.9.2": + version "3.9.2" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.9.2.tgz#ffab7922631c7e0febcb54e6d499f648bf8a89eb" + integrity sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ== dependencies: - "@docusaurus/logger" "3.8.1" - "@docusaurus/utils" "3.8.1" - "@docusaurus/utils-common" "3.8.1" - fs-extra "^11.2.0" - joi "^17.9.2" - js-yaml "^4.1.0" - lodash "^4.17.21" - tslib "^2.6.0" - -"@docusaurus/utils@3.6.3": - version "3.6.3" - resolved "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.6.3.tgz" - integrity sha512-0R/FR3bKVl4yl8QwbL4TYFfR+OXBRpVUaTJdENapBGR3YMwfM6/JnhGilWQO8AOwPJGtGoDK7ib8+8UF9f3OZQ== - dependencies: - "@docusaurus/logger" "3.6.3" - "@docusaurus/types" "3.6.3" - "@docusaurus/utils-common" "3.6.3" - "@svgr/webpack" "^8.1.0" - escape-string-regexp "^4.0.0" - file-loader "^6.2.0" - fs-extra "^11.1.1" - github-slugger "^1.5.0" - globby "^11.1.0" - gray-matter "^4.0.3" - jiti "^1.20.0" - js-yaml "^4.1.0" - lodash "^4.17.21" - micromatch "^4.0.5" - prompts "^2.4.2" - resolve-pathname "^3.0.0" - shelljs "^0.8.5" - tslib "^2.6.0" - url-loader "^4.1.1" - utility-types "^3.10.0" - webpack "^5.88.1" - -"@docusaurus/utils@3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.8.1.tgz#2ac1e734106e2f73dbd0f6a8824d525f9064e9f0" - integrity sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ== - dependencies: - "@docusaurus/logger" "3.8.1" - "@docusaurus/types" "3.8.1" - "@docusaurus/utils-common" "3.8.1" + "@docusaurus/logger" "3.9.2" + "@docusaurus/types" "3.9.2" + "@docusaurus/utils-common" "3.9.2" escape-string-regexp "^4.0.0" execa "5.1.1" file-loader "^6.2.0" @@ -3706,19 +3665,14 @@ resolved "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz" integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== -"@iconify/utils@^2.1.32": - version "2.2.0" - resolved "https://registry.npmjs.org/@iconify/utils/-/utils-2.2.0.tgz" - integrity sha512-9A5eZQV9eKlNCXlI/SgYsGRS7YmGmB1oAsRpNVIYBmIzGJRgH+hfG+lo4069s+GFWFNnBAtDg10c53vQZBLfnA== +"@iconify/utils@^3.0.1": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@iconify/utils/-/utils-3.1.0.tgz#fb41882915f97fee6f91a2fbb8263e8772ca0438" + integrity sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw== dependencies: - "@antfu/install-pkg" "^0.4.1" - "@antfu/utils" "^0.7.10" + "@antfu/install-pkg" "^1.1.0" "@iconify/types" "^2.0.0" - debug "^4.4.0" - globals "^15.13.0" - kolorist "^1.8.0" - local-pkg "^0.5.1" - mlly "^1.7.3" + mlly "^1.8.0" "@inkeep/cxkit-color-mode@0.5.91": version "0.5.91" @@ -3901,6 +3855,51 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jsonjoy.com/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" + integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== + +"@jsonjoy.com/buffers@^1.0.0", "@jsonjoy.com/buffers@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz#8d99c7f67eaf724d3428dfd9826c6455266a5c83" + integrity sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA== + +"@jsonjoy.com/codegen@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz#5c23f796c47675f166d23b948cdb889184b93207" + integrity sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g== + +"@jsonjoy.com/json-pack@^1.11.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz#93f8dd57fe3a3a92132b33d1eb182dcd9e7629fa" + integrity sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg== + dependencies: + "@jsonjoy.com/base64" "^1.1.2" + "@jsonjoy.com/buffers" "^1.2.0" + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/json-pointer" "^1.0.2" + "@jsonjoy.com/util" "^1.9.0" + hyperdyperid "^1.2.0" + thingies "^2.5.0" + tree-dump "^1.1.0" + +"@jsonjoy.com/json-pointer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz#049cb530ac24e84cba08590c5e36b431c4843408" + integrity sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg== + dependencies: + "@jsonjoy.com/codegen" "^1.0.0" + "@jsonjoy.com/util" "^1.9.0" + +"@jsonjoy.com/util@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.9.0.tgz#7ee95586aed0a766b746cd8d8363e336c3c47c46" + integrity sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ== + dependencies: + "@jsonjoy.com/buffers" "^1.0.0" + "@jsonjoy.com/codegen" "^1.0.0" + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.5" resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz" @@ -3942,12 +3941,12 @@ dependencies: "@types/mdx" "^2.0.0" -"@mermaid-js/parser@^0.3.0": - version "0.3.0" - resolved "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz" - integrity sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA== +"@mermaid-js/parser@^0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-0.6.3.tgz#3ce92dad2c5d696d29e11e21109c66a7886c824e" + integrity sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA== dependencies: - langium "3.0.0" + langium "3.3.1" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -4085,6 +4084,11 @@ resolved "https://registry.npmjs.org/@offchainlabs/prettier-config/-/prettier-config-0.2.1.tgz" integrity sha512-VjE4Hahx9ipTheXJVlt7tFtHeMVvlYUkKNT4uxnAK4i4qrLcqhlm/4GE9mueOS/Syi2/gzDKvNH0nk6+ts2IrA== +"@opentelemetry/api@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" + integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== + "@philpl/buble@^0.19.7": version "0.19.7" resolved "https://registry.npmjs.org/@philpl/buble/-/buble-0.19.7.tgz" @@ -4830,6 +4834,11 @@ micromark-util-character "^1.1.0" micromark-util-symbol "^1.0.1" +"@standard-schema/spec@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + "@svgr/babel-plugin-add-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz" @@ -4984,9 +4993,9 @@ "@types/connect" "*" "@types/node" "*" -"@types/bonjour@^3.5.9": +"@types/bonjour@^3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" @@ -5001,9 +5010,9 @@ "@types/node" "*" "@types/responselike" "^1.0.0" -"@types/connect-history-api-fallback@^1.3.5": +"@types/connect-history-api-fallback@^1.5.4": version "1.5.4" - resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== dependencies: "@types/express-serve-static-core" "*" @@ -5283,7 +5292,17 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@*", "@types/express@^4.17.13": +"@types/express-serve-static-core@^4.17.21": + version "4.19.7" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz#f1d306dcc03b1aafbfb6b4fe684cce8a31cffc10" + integrity sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": version "4.17.21" resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -5293,6 +5312,16 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/express@^4.17.21": + version "4.17.25" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.25.tgz#070c8c73a6fee6936d65c195dbbfb7da5026649b" + integrity sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "^1" + "@types/geojson@*": version "7946.0.15" resolved "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz" @@ -5363,7 +5392,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -5443,11 +5472,6 @@ dependencies: undici-types "~5.26.4" -"@types/parse-json@^4.0.0": - version "4.0.2" - resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" - integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== - "@types/prismjs@^1.26.0": version "1.26.3" resolved "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz" @@ -5533,10 +5557,10 @@ dependencies: "@types/node" "*" -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== "@types/sax@^1.2.1": version "1.2.7" @@ -5558,14 +5582,22 @@ "@types/mime" "^1" "@types/node" "*" -"@types/serve-index@^1.9.1": +"@types/send@<1": + version "0.17.6" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.6.tgz#aeb5385be62ff58a52cd5459daa509ae91651d25" + integrity sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.4": version "1.9.4" - resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" -"@types/serve-static@*", "@types/serve-static@^1.13.10": +"@types/serve-static@*": version "1.15.7" resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz" integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== @@ -5574,9 +5606,18 @@ "@types/node" "*" "@types/send" "*" -"@types/sockjs@^0.3.33": +"@types/serve-static@^1", "@types/serve-static@^1.15.5": + version "1.15.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.10.tgz#768169145a778f8f5dfcb6360aead414a3994fee" + integrity sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "<1" + +"@types/sockjs@^0.3.36": version "0.3.36" - resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" @@ -5601,10 +5642,10 @@ resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz" integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== -"@types/ws@^8.5.5": - version "8.5.10" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz" - integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== +"@types/ws@^8.5.10": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== dependencies: "@types/node" "*" @@ -5992,7 +6033,12 @@ acorn@^8.14.0: resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== -address@^1.0.1, address@^1.1.2: +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +address@^1.0.1: version "1.2.2" resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== @@ -6017,6 +6063,16 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ai@5.0.115, ai@^5.0.30: + version "5.0.115" + resolved "https://registry.yarnpkg.com/ai/-/ai-5.0.115.tgz#a04ff38a340bc4e3cd7e7f444577fa4eb36d129a" + integrity sha512-aVuHx0orGxXvhyL7oXUyW8TnWQE6Al8f3Bl6VZjz0WHMV+WaACHPkSyvQ3wje2QCUGzdl5DBF5d+OaXyghPQyg== + dependencies: + "@ai-sdk/gateway" "2.0.22" + "@ai-sdk/provider" "2.0.0" + "@ai-sdk/provider-utils" "3.0.19" + "@opentelemetry/api" "1.9.0" + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" @@ -6024,7 +6080,7 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: +ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== @@ -6036,7 +6092,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.2, ajv@^6.12.5: +ajv@^6.12.5: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -6056,33 +6112,32 @@ ajv@^8.0.0, ajv@^8.9.0: require-from-string "^2.0.2" uri-js "^4.4.1" -algoliasearch-helper@^3.13.3: - version "3.19.0" - resolved "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.19.0.tgz" - integrity sha512-AaSb5DZDMZmDQyIy6lf4aL0OZGgyIdqvLIIvSuVQOIOqfhrYSY7TvotIFI2x0Q3cP3xUpTd7lI1astUC4aXBJw== +algoliasearch-helper@^3.26.0: + version "3.26.1" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.26.1.tgz#5b7f0874a2751c3d6de675d5403d8fa2f015023f" + integrity sha512-CAlCxm4fYBXtvc5MamDzP6Svu8rW4z9me4DCBY1rQ2UDJ0u0flWmusQ8M3nOExZsLLRcUwUPoRAPMrhzOG3erw== dependencies: "@algolia/events" "^4.0.1" -algoliasearch@^4.18.0, algoliasearch@^4.19.1: - version "4.23.3" - resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz" - integrity sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg== - dependencies: - "@algolia/cache-browser-local-storage" "4.23.3" - "@algolia/cache-common" "4.23.3" - "@algolia/cache-in-memory" "4.23.3" - "@algolia/client-account" "4.23.3" - "@algolia/client-analytics" "4.23.3" - "@algolia/client-common" "4.23.3" - "@algolia/client-personalization" "4.23.3" - "@algolia/client-search" "4.23.3" - "@algolia/logger-common" "4.23.3" - "@algolia/logger-console" "4.23.3" - "@algolia/recommend" "4.23.3" - "@algolia/requester-browser-xhr" "4.23.3" - "@algolia/requester-common" "4.23.3" - "@algolia/requester-node-http" "4.23.3" - "@algolia/transporter" "4.23.3" +algoliasearch@^5.28.0, algoliasearch@^5.37.0: + version "5.46.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.46.1.tgz#3b3de480398f3b559632489a1778d5c581183794" + integrity sha512-39ol8Ulqb3MntofkXHlrcXKyU8BU0PXvQrXPBIX6eXj/EO4VT7651mhGVORI2oF8ydya9nFzT3fYDoqme/KL6w== + dependencies: + "@algolia/abtesting" "1.12.1" + "@algolia/client-abtesting" "5.46.1" + "@algolia/client-analytics" "5.46.1" + "@algolia/client-common" "5.46.1" + "@algolia/client-insights" "5.46.1" + "@algolia/client-personalization" "5.46.1" + "@algolia/client-query-suggestions" "5.46.1" + "@algolia/client-search" "5.46.1" + "@algolia/ingestion" "1.46.1" + "@algolia/monitoring" "1.46.1" + "@algolia/recommend" "5.46.1" + "@algolia/requester-browser-xhr" "5.46.1" + "@algolia/requester-fetch" "5.46.1" + "@algolia/requester-node-http" "5.46.1" altcha-lib@^1.2.0: version "1.3.0" @@ -6201,11 +6256,6 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - autoprefixer@^10.4.19: version "10.4.19" resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz" @@ -6218,6 +6268,17 @@ autoprefixer@^10.4.19: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +autoprefixer@^10.4.22: + version "10.4.23" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.23.tgz#c6aa6db8e7376fcd900f9fd79d143ceebad8c4e6" + integrity sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA== + dependencies: + browserslist "^4.28.1" + caniuse-lite "^1.0.30001760" + fraction.js "^5.3.4" + picocolors "^1.1.1" + postcss-value-parser "^4.2.0" + babel-loader@^9.2.1: version "9.2.1" resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz" @@ -6275,6 +6336,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +baseline-browser-mapping@^2.9.0: + version "2.9.9" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.9.tgz#68c17013c33ba9e8264c5f2ae107d506228428ee" + integrity sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg== + batch@0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" @@ -6310,28 +6376,28 @@ bn.js@^5.2.1: resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.20.2: - version "1.20.2" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz" - integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== +body-parser@~1.20.3: + version "1.20.4" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f" + integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA== dependencies: - bytes "3.1.2" + bytes "~3.1.2" content-type "~1.0.5" debug "2.6.9" depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.2" + destroy "~1.2.0" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + on-finished "~2.4.1" + qs "~6.14.0" + raw-body "~2.5.3" type-is "~1.6.18" - unpipe "1.0.0" + unpipe "~1.0.0" -bonjour-service@^1.0.11: - version "1.2.1" - resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz" - integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== +bonjour-service@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.3.0.tgz#80d867430b5a0da64e82a8047fc1e355bdb71722" + integrity sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA== dependencies: fast-deep-equal "^3.1.3" multicast-dns "^7.2.5" @@ -6396,7 +6462,7 @@ brorand@^1.1.0: resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.22.2, browserslist@^4.23.0: +browserslist@^4.0.0, browserslist@^4.21.10, browserslist@^4.22.2, browserslist@^4.23.0: version "4.23.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz" integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== @@ -6406,7 +6472,7 @@ browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^ node-releases "^2.0.14" update-browserslist-db "^1.0.13" -browserslist@^4.23.1, browserslist@^4.24.0, browserslist@^4.24.2: +browserslist@^4.24.0, browserslist@^4.24.2: version "4.24.2" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz" integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== @@ -6416,19 +6482,37 @@ browserslist@^4.23.1, browserslist@^4.24.0, browserslist@^4.24.2: node-releases "^2.0.18" update-browserslist-db "^1.1.1" +browserslist@^4.28.0, browserslist@^4.28.1: + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== + dependencies: + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== -bytes@3.1.2: +bytes@~3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cacheable-lookup@^5.0.3: @@ -6475,7 +6559,7 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: es-errors "^1.3.0" function-bind "^1.1.2" -call-bind@^1.0.5, call-bind@^1.0.7: +call-bind@^1.0.5: version "1.0.7" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== @@ -6486,6 +6570,14 @@ call-bind@^1.0.5, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -6529,6 +6621,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz" integrity sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg== +caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001760: + version "1.0.30001760" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz#bdd1960fafedf8d5f04ff16e81460506ff9b798f" + integrity sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw== + ccount@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" @@ -6543,7 +6640,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -6640,7 +6737,7 @@ chevrotain@~11.0.3: "@chevrotain/utils" "11.0.3" lodash-es "4.17.21" -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.3: +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -6677,7 +6774,7 @@ classnames@^2.5.1: resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== -clean-css@^5.2.2, clean-css@^5.3.2, clean-css@~5.3.2: +clean-css@^5.2.2, clean-css@^5.3.3, clean-css@~5.3.2: version "5.3.3" resolved "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz" integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== @@ -6899,9 +6996,9 @@ content-disposition@0.5.2: resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz" integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== -content-disposition@0.5.4: +content-disposition@~0.5.4: version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" @@ -6916,20 +7013,15 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie-signature@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.7.tgz#ab5dd7ab757c54e60f37ef6550f481c426d10454" + integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA== -copy-text-to-clipboard@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz" - integrity sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q== +cookie@~0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== copy-webpack-plugin@^11.0.0: version "11.0.0" @@ -6991,17 +7083,6 @@ cose-base@^2.2.0: dependencies: layout-base "^2.0.0" -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.7.2" - cosmiconfig@^8.1.3, cosmiconfig@^8.3.5: version "8.3.6" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz" @@ -7054,18 +7135,18 @@ css-declaration-sorter@^7.2.0: resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz" integrity sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow== -css-has-pseudo@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.1.tgz" - integrity sha512-EOcoyJt+OsuKfCADgLT7gADZI5jMzIe/AeI6MeAYKiFBDmNmM7kk46DtSfMj5AohUJisqVzopBpnQTlvbyaBWg== +css-has-pseudo@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-7.0.3.tgz#a5ee2daf5f70a2032f3cefdf1e36e7f52a243873" + integrity sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA== dependencies: "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" postcss-value-parser "^4.2.0" -css-loader@^6.8.1: +css-loader@^6.11.0: version "6.11.0" - resolved "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba" integrity sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g== dependencies: icss-utils "^5.1.0" @@ -7146,10 +7227,10 @@ css-what@^6.0.1, css-what@^6.1.0: resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -cssdb@^8.2.1: - version "8.2.3" - resolved "https://registry.npmjs.org/cssdb/-/cssdb-8.2.3.tgz" - integrity sha512-9BDG5XmJrJQQnJ51VFxXCAtpZ5ebDlAREmO8sxMOVU0aSxN/gocbctjIG5LMh3WBUq+xTlb/jw2LoljBEqraTA== +cssdb@^8.5.2: + version "8.5.2" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.5.2.tgz#8a8c16c43785e32749453e589f18debcd936c7d1" + integrity sha512-Pmoj9RmD8RIoIzA2EQWO4D4RMeDts0tgAH0VXdlNdxjuBGI3a9wMOIcUwaPNmD4r2qtIa06gqkIf7sECl+cBCg== cssesc@^3.0.0: version "3.0.0" @@ -7244,16 +7325,11 @@ cytoscape-fcose@^2.2.0: dependencies: cose-base "^2.2.0" -cytoscape@^3.28.1: +cytoscape@^3.28.1, cytoscape@^3.29.3: version "3.33.1" resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.33.1.tgz#449e05d104b760af2912ab76482d24c01cdd4c97" integrity sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ== -cytoscape@^3.29.2: - version "3.30.4" - resolved "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz" - integrity sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A== - "d3-array@1 - 2": version "2.12.1" resolved "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz" @@ -7533,15 +7609,20 @@ dagre-d3-es@7.0.10: d3 "^7.8.2" lodash-es "^4.17.21" -dagre-d3-es@7.0.11: - version "7.0.11" - resolved "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz" - integrity sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw== +dagre-d3-es@7.0.13: + version "7.0.13" + resolved "https://registry.yarnpkg.com/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz#acfb4b449f6dcdd48d8ea8081a6d8c59bc8128c3" + integrity sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q== dependencies: d3 "^7.9.0" lodash-es "^4.17.21" -dayjs@^1.11.10, dayjs@^1.11.7: +dayjs@^1.11.18: + version "1.11.19" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" + integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== + +dayjs@^1.11.7: version "1.11.13" resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== @@ -7551,7 +7632,7 @@ debounce@^1.2.1: resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@2.6.9, debug@^2.6.0: +debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -7591,17 +7672,23 @@ deep-extend@^0.6.0: resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deepmerge@^4.2.2, deepmerge@^4.3.1: +deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== +default-browser-id@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.1.tgz#f7a7ccb8f5104bf8e0f71ba3b1ccfa5eafdb21e8" + integrity sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q== + +default-browser@^5.2.1: + version "5.4.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.4.0.tgz#b55cf335bb0b465dd7c961a02cd24246aa434287" + integrity sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg== dependencies: - execa "^5.0.0" + bundle-name "^4.1.0" + default-browser-id "^5.0.0" defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: version "2.0.1" @@ -7622,6 +7709,11 @@ define-lazy-prop@^2.0.0: resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + define-properties@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" @@ -7631,20 +7723,6 @@ define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -del@^6.1.1: - version "6.1.1" - resolved "https://registry.npmjs.org/del/-/del-6.1.1.tgz" - integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== - dependencies: - globby "^11.0.1" - graceful-fs "^4.2.4" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.2" - p-map "^4.0.0" - rimraf "^3.0.2" - slash "^3.0.0" - delaunator@5: version "5.0.1" resolved "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz" @@ -7657,7 +7735,7 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -7677,7 +7755,7 @@ dequal@^2.0.0, dequal@^2.0.3: resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -destroy@1.2.0: +destroy@1.2.0, destroy@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -7692,14 +7770,6 @@ detect-node@^2.0.4: resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -detect-port-alt@^1.1.6: - version "1.1.6" - resolved "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz" - integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== - dependencies: - address "^1.0.1" - debug "^2.6.0" - detect-port@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz" @@ -7802,10 +7872,10 @@ domhandler@^5.0.2, domhandler@^5.0.3: resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.6.tgz#43c714a94c6a7b8801850f82e756685300a027e2" integrity sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ== -dompurify@^3.2.1: - version "3.2.3" - resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz" - integrity sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA== +dompurify@^3.2.5: + version "3.3.1" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.3.1.tgz#c7e1ddebfe3301eacd6c0c12a4af284936dbbb86" + integrity sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q== optionalDependencies: "@types/trusted-types" "^2.0.7" @@ -7881,6 +7951,11 @@ electron-to-chromium@^1.4.668: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.754.tgz" integrity sha512-7Kr5jUdns5rL/M9wFFmMZAgFDuL2YOnanFH4OI4iFzUqyh3XOL7nAGbSlSMZdzKMIyyTpNSbqZsWG9odwLeKvA== +electron-to-chromium@^1.5.263: + version "1.5.267" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" + integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== + electron-to-chromium@^1.5.41: version "1.5.72" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.72.tgz" @@ -7929,10 +8004,10 @@ emoticon@^4.0.1: resolved "https://registry.npmjs.org/emoticon/-/emoticon-4.0.1.tgz" integrity sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw== -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== end-of-stream@^1.1.0: version "1.4.4" @@ -8230,7 +8305,12 @@ events@^3.2.0: resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -execa@5.1.1, execa@^5.0.0: +eventsource-parser@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + +execa@5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -8252,32 +8332,32 @@ express@^4.17.3: dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.2" - content-disposition "0.5.4" + body-parser "~1.20.3" + content-disposition "~0.5.4" content-type "~1.0.4" - cookie "0.6.0" - cookie-signature "1.0.6" + cookie "~0.7.1" + cookie-signature "~1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" + finalhandler "~1.3.1" + fresh "~0.5.2" + http-errors "~2.0.0" + merge-descriptors "1.0.3" methods "~1.1.2" - on-finished "2.4.1" + on-finished "~2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "~0.1.12" proxy-addr "~2.0.7" - qs "6.11.0" + qs "~6.14.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "~0.19.0" + serve-static "~1.16.2" setprototypeof "1.2.0" - statuses "2.0.1" + statuses "~2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -8370,11 +8450,6 @@ file-loader@^6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" -filesize@^8.0.6: - version "8.0.7" - resolved "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz" - integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" @@ -8382,41 +8457,26 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@~1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88" + integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg== dependencies: debug "2.6.9" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" - on-finished "2.4.1" + on-finished "~2.4.1" parseurl "~1.3.3" - statuses "2.0.1" + statuses "~2.0.2" unpipe "~1.0.0" find-cache-dir@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz" - integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== - dependencies: - common-path-prefix "^3.0.0" - pkg-dir "^7.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz" + integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" + common-path-prefix "^3.0.0" + pkg-dir "^7.0.0" find-up@^6.3.0: version "6.3.0" @@ -8452,25 +8512,6 @@ foreground-child@^3.3.1: cross-spawn "^7.0.6" signal-exit "^4.0.1" -fork-ts-checker-webpack-plugin@^6.5.0: - version "6.5.3" - resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz" - integrity sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ== - dependencies: - "@babel/code-frame" "^7.8.3" - "@types/json-schema" "^7.0.5" - chalk "^4.1.0" - chokidar "^3.4.2" - cosmiconfig "^6.0.0" - deepmerge "^4.2.2" - fs-extra "^9.0.0" - glob "^7.1.6" - memfs "^3.1.2" - minimatch "^3.0.4" - schema-utils "2.7.0" - semver "^7.3.2" - tapable "^1.0.0" - form-data-encoder@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" @@ -8514,9 +8555,14 @@ fraction.js@^4.3.7: resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -fresh@0.5.2: +fraction.js@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-5.3.4.tgz#8c0fcc6a9908262df4ed197427bdeef563e0699a" + integrity sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ== + +fresh@~0.5.2: version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fs-extra@^11.1.1, fs-extra@^11.2.0: @@ -8528,21 +8574,6 @@ fs-extra@^11.1.1, fs-extra@^11.2.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0: - version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-monkey@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz" - integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -8563,7 +8594,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-intrinsic@^1.2.4, get-intrinsic@^1.2.6: +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -8635,6 +8666,11 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" +glob-to-regex.js@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz#2b323728271d133830850e32311f40766c5f6413" + integrity sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ== + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" @@ -8651,7 +8687,7 @@ glob@^10.3.10: minipass "^7.0.4" path-scurry "^1.10.2" -glob@^7.0.0, glob@^7.1.3, glob@^7.1.6, glob@^7.2.0: +glob@^7.2.0: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -8682,33 +8718,12 @@ global-dirs@^3.0.0: dependencies: ini "2.0.0" -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^15.13.0: - version "15.13.0" - resolved "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz" - integrity sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g== - -globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: +globby@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -9098,11 +9113,6 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -html-entities@^2.3.2: - version "2.5.2" - resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz" - integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== - html-escaper@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" @@ -9197,17 +9207,6 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - http-errors@~1.6.2: version "1.6.3" resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" @@ -9218,15 +9217,26 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-errors@~2.0.0, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== -http-proxy-middleware@^2.0.3: - version "2.0.6" - resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz" - integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== +http-proxy-middleware@^2.0.9: + version "2.0.9" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef" + integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q== dependencies: "@types/http-proxy" "^1.17.8" http-proxy "^1.18.1" @@ -9281,12 +9291,10 @@ husky@^9.1.7: resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== iconv-lite@0.6: version "0.6.3" @@ -9295,6 +9303,13 @@ iconv-lite@0.6: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@~0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" @@ -9310,29 +9325,17 @@ ignore@~7.0.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== -image-size@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.2.tgz#d778b6d0ab75b2737c1556dd631652eb963bc486" - integrity sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg== - dependencies: - queue "6.0.2" - image-size@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/image-size/-/image-size-2.0.2.tgz#84a7b43704db5736f364bf0d1b029821299b4bdc" integrity sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w== -immer@^9.0.7: - version "9.0.21" - resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz" - integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== - immutable@^4.0.0: version "4.3.5" resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz" integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw== -import-fresh@^3.1.0, import-fresh@^3.3.0: +import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -9368,7 +9371,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -9383,7 +9386,7 @@ ini@2.0.0: resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -9413,11 +9416,6 @@ internmap@^1.0.0: resolved "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz" integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== -interpret@^1.0.0: - version "1.4.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" @@ -9430,10 +9428,10 @@ ipaddr.js@1.9.1: resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: - version "2.2.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== +ipaddr.js@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.3.0.tgz#71dce70e1398122208996d1c22f2ba46a24b1abc" + integrity sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg== is-alphabetical@^1.0.0: version "1.0.4" @@ -9502,6 +9500,11 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extendable@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" @@ -9534,6 +9537,13 @@ is-hexadecimal@^2.0.0: resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz" integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-installed-globally@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz" @@ -9542,6 +9552,11 @@ is-installed-globally@^0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-network-error@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.3.0.tgz#2ce62cbca444abd506f8a900f39d20b898d37512" + integrity sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw== + is-npm@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz" @@ -9562,11 +9577,6 @@ is-obj@^2.0.0: resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" @@ -9601,11 +9611,6 @@ is-regexp@^1.0.0: resolved "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== -is-root@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz" - integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" @@ -9628,6 +9633,13 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + is-yarn-global@^0.4.0: version "0.4.1" resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz" @@ -9783,6 +9795,11 @@ json-schema-traverse@^1.0.0: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json5@^2.1.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" @@ -9814,6 +9831,13 @@ katex@^0.16.0: dependencies: commander "^8.3.0" +katex@^0.16.22: + version "0.16.27" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.27.tgz#4ecf6f620e0ca1c1a5de722e85fcdcec49086a48" + integrity sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw== + dependencies: + commander "^8.3.0" + katex@^0.16.9: version "0.16.10" resolved "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz" @@ -9853,15 +9877,10 @@ klona@^2.0.4: resolved "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz" integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== -kolorist@^1.8.0: - version "1.8.0" - resolved "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz" - integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== - -langium@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz" - integrity sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg== +langium@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/langium/-/langium-3.3.1.tgz#da745a40d5ad8ee565090fed52eaee643be4e591" + integrity sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w== dependencies: chevrotain "~11.0.3" chevrotain-allstar "~0.3.0" @@ -9876,13 +9895,13 @@ latest-version@^7.0.0: dependencies: package-json "^8.1.0" -launch-editor@^2.6.0: - version "2.6.1" - resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz" - integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== +launch-editor@^2.6.1: + version "2.12.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.12.0.tgz#cc740f4e0263a6b62ead2485f9896e545321f817" + integrity sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg== dependencies: - picocolors "^1.0.0" - shell-quote "^1.8.1" + picocolors "^1.1.1" + shell-quote "^1.8.3" layout-base@^1.0.0: version "1.0.2" @@ -9930,34 +9949,6 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^3.2.0: - version "3.2.1" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz" - integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== - -local-pkg@^0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz" - integrity sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ== - dependencies: - mlly "^1.7.3" - pkg-types "^1.2.1" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - locate-path@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz" @@ -10129,11 +10120,6 @@ markdownlint@~0.38.0: micromark-extension-math "3.1.0" micromark-util-types "2.0.2" -marked@^13.0.2: - version "13.0.3" - resolved "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz" - integrity sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA== - marked@^15.0.7: version "15.0.11" resolved "https://registry.npmjs.org/marked/-/marked-15.0.11.tgz" @@ -10444,12 +10430,17 @@ media-typer@0.3.0: resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memfs@^3.1.2, memfs@^3.4.3: - version "3.6.0" - resolved "https://registry.npmjs.org/memfs/-/memfs-3.6.0.tgz" - integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== +memfs@^4.43.1: + version "4.51.1" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.51.1.tgz#25945de4a90d1573945105e187daa9385e1bca73" + integrity sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ== dependencies: - fs-monkey "^1.0.4" + "@jsonjoy.com/json-pack" "^1.11.0" + "@jsonjoy.com/util" "^1.9.0" + glob-to-regex.js "^1.0.1" + thingies "^2.5.0" + tree-dump "^1.0.3" + tslib "^2.0.0" merge-anything@5.1.7: version "5.1.7" @@ -10458,10 +10449,10 @@ merge-anything@5.1.7: dependencies: is-what "^4.1.8" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" @@ -10473,31 +10464,31 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mermaid@>=10.4: - version "11.4.1" - resolved "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz" - integrity sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A== +mermaid@>=11.6.0: + version "11.12.2" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-11.12.2.tgz#48bbdb9f724bc2191e2128e1403bf964fff2bc3d" + integrity sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w== dependencies: - "@braintree/sanitize-url" "^7.0.1" - "@iconify/utils" "^2.1.32" - "@mermaid-js/parser" "^0.3.0" + "@braintree/sanitize-url" "^7.1.1" + "@iconify/utils" "^3.0.1" + "@mermaid-js/parser" "^0.6.3" "@types/d3" "^7.4.3" - cytoscape "^3.29.2" + cytoscape "^3.29.3" cytoscape-cose-bilkent "^4.1.0" cytoscape-fcose "^2.2.0" d3 "^7.9.0" d3-sankey "^0.12.3" - dagre-d3-es "7.0.11" - dayjs "^1.11.10" - dompurify "^3.2.1" - katex "^0.16.9" + dagre-d3-es "7.0.13" + dayjs "^1.11.18" + dompurify "^3.2.5" + katex "^0.16.22" khroma "^2.1.0" lodash-es "^4.17.21" - marked "^13.0.2" + marked "^16.2.1" roughjs "^4.6.6" - stylis "^4.3.1" + stylis "^4.3.6" ts-dedent "^2.2.0" - uuid "^9.0.1" + uuid "^11.1.0" mermaid@^10.0.0: version "10.9.4" @@ -11238,6 +11229,11 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-db@~1.33.0: version "1.33.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" @@ -11250,13 +11246,20 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime-types@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" @@ -11282,10 +11285,10 @@ mimic-response@^4.0.0: resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz" integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== -mini-css-extract-plugin@^2.9.1: - version "2.9.2" - resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz" - integrity sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w== +mini-css-extract-plugin@^2.9.2: + version "2.9.4" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz#cafa1a42f8c71357f49cd1566810d74ff1cb0200" + integrity sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ== dependencies: schema-utils "^4.0.0" tapable "^2.2.1" @@ -11300,7 +11303,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: +minimatch@3.1.2, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -11350,15 +11353,15 @@ minipass@^7.1.2: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mlly@^1.7.2, mlly@^1.7.3: - version "1.7.3" - resolved "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz" - integrity sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A== +mlly@^1.7.4, mlly@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.8.0.tgz#e074612b938af8eba1eaf43299cbc89cb72d824e" + integrity sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== dependencies: - acorn "^8.14.0" - pathe "^1.1.2" - pkg-types "^1.2.1" - ufo "^1.5.4" + acorn "^8.15.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + ufo "^1.6.1" mri@^1.1.0: version "1.2.0" @@ -11402,6 +11405,11 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + nanoid@^3.3.7: version "3.3.7" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz" @@ -11462,6 +11470,11 @@ node-releases@^2.0.18: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + non-layered-tidy-tree-layout@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz#57d35d13c356643fc296a55fb11ac15e74da7804" @@ -11519,10 +11532,10 @@ object-assign@^4.0.1, object-assign@^4.1.1: resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1: - version "1.13.1" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-keys@^1.1.1: version "1.1.1" @@ -11544,9 +11557,9 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1: +on-finished@^2.4.1, on-finished@~2.4.1: version "2.4.1" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" @@ -11570,7 +11583,17 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^8.0.9, open@^8.4.0: +open@^10.0.3: + version "10.2.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.2.0.tgz#b9d855be007620e80b6fb05fac98141fe62db73c" + integrity sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + wsl-utils "^0.1.0" + +open@^8.4.0: version "8.4.2" resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== @@ -11617,20 +11640,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - p-limit@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz" @@ -11638,20 +11647,6 @@ p-limit@^4.0.0: dependencies: yocto-queue "^1.0.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - p-locate@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz" @@ -11674,12 +11669,13 @@ p-queue@^6.6.2: eventemitter3 "^4.0.4" p-timeout "^3.2.0" -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== +p-retry@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.1.tgz#81828f8dc61c6ef5a800585491572cc9892703af" + integrity sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ== dependencies: - "@types/retry" "0.12.0" + "@types/retry" "0.12.2" + is-network-error "^1.0.0" retry "^0.13.1" p-timeout@^3.2.0: @@ -11689,11 +11685,6 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - package-json-from-dist@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" @@ -11709,10 +11700,10 @@ package-json@^8.1.0: registry-url "^6.0.0" semver "^7.3.7" -package-manager-detector@^0.2.0: - version "0.2.7" - resolved "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.7.tgz" - integrity sha512-g4+387DXDKlZzHkP+9FLt8yKj8+/3tOkPv7DVTJGGRm00RkEWgqbFstX1mXJ4M0VDYhUqsTOiISqNOJnhAu3PQ== +package-manager-detector@^1.3.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/package-manager-detector/-/package-manager-detector-1.6.0.tgz#70d0cf0aa02c877eeaf66c4d984ede0be9130734" + integrity sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA== param-case@^3.0.4: version "3.0.4" @@ -11755,7 +11746,7 @@ parse-entities@^4.0.0: is-decimal "^2.0.0" is-hexadecimal "^2.0.0" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -11803,16 +11794,6 @@ path-data-parser@0.1.0, path-data-parser@^0.1.0: resolved "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz" integrity sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w== -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - path-exists@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz" @@ -11854,11 +11835,6 @@ path-scurry@^2.0.0: lru-cache "^11.0.0" minipass "^7.1.2" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - path-to-regexp@3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz" @@ -11871,6 +11847,11 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" +path-to-regexp@~0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" @@ -11925,21 +11906,14 @@ pkg-dir@^7.0.0: dependencies: find-up "^6.3.0" -pkg-types@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz" - integrity sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw== +pkg-types@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== dependencies: confbox "^0.1.8" - mlly "^1.7.2" - pathe "^1.1.2" - -pkg-up@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz" - integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== - dependencies: - find-up "^3.0.0" + mlly "^1.7.4" + pathe "^2.0.1" points-on-curve@0.2.0, points-on-curve@^0.2.0: version "0.2.0" @@ -11976,15 +11950,15 @@ postcss-clamp@^4.1.0: dependencies: postcss-value-parser "^4.2.0" -postcss-color-functional-notation@^7.0.6: - version "7.0.6" - resolved "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.6.tgz" - integrity sha512-wLXvm8RmLs14Z2nVpB4CWlnvaWPRcOZFltJSlcbYwSJ1EDZKsKDhPKIMecCnuU054KSmlmubkqczmm6qBPCBhA== +postcss-color-functional-notation@^7.0.12: + version "7.0.12" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.12.tgz#9a3df2296889e629fde18b873bb1f50a4ecf4b83" + integrity sha512-TLCW9fN5kvO/u38/uesdpbx3e8AkTYhMvDZYa9JpmImWuTE99bDQ7GU7hdOADIZsiI9/zuxfAJxny/khknp1Zw== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" postcss-color-hex-alpha@^10.0.0: @@ -12021,35 +11995,35 @@ postcss-convert-values@^6.1.0: browserslist "^4.23.0" postcss-value-parser "^4.2.0" -postcss-custom-media@^11.0.5: - version "11.0.5" - resolved "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.5.tgz" - integrity sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ== +postcss-custom-media@^11.0.6: + version "11.0.6" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz#6b450e5bfa209efb736830066682e6567bd04967" + integrity sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.4" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/media-query-list-parser" "^4.0.2" + "@csstools/cascade-layer-name-parser" "^2.0.5" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/media-query-list-parser" "^4.0.3" -postcss-custom-properties@^14.0.4: - version "14.0.4" - resolved "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.4.tgz" - integrity sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A== +postcss-custom-properties@^14.0.6: + version "14.0.6" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz#1af73a650bf115ba052cf915287c9982825fc90e" + integrity sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.4" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/cascade-layer-name-parser" "^2.0.5" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -postcss-custom-selectors@^8.0.4: - version "8.0.4" - resolved "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.4.tgz" - integrity sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg== +postcss-custom-selectors@^8.0.5: + version "8.0.5" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz#9448ed37a12271d7ab6cb364b6f76a46a4a323e8" + integrity sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg== dependencies: - "@csstools/cascade-layer-name-parser" "^2.0.4" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" + "@csstools/cascade-layer-name-parser" "^2.0.5" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" postcss-selector-parser "^7.0.0" postcss-dir-pseudo-class@^9.0.1: @@ -12086,12 +12060,12 @@ postcss-discard-unused@^6.0.5: dependencies: postcss-selector-parser "^6.0.16" -postcss-double-position-gradients@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.0.tgz" - integrity sha512-JkIGah3RVbdSEIrcobqj4Gzq0h53GG4uqDPsho88SgY84WnpkTpI0k50MFK/sX7XqVisZ6OqUfFnoUO6m1WWdg== +postcss-double-position-gradients@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.4.tgz#b482d08b5ced092b393eb297d07976ab482d4cad" + integrity sha512-m6IKmxo7FxSP5nF2l63QbCC3r+bWpFUWmZXZf096WxG0m7Vl1Q1+ruFOhpdDRmKrRS+S3Jtk+TVk/7z0+BVK6g== dependencies: - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" @@ -12127,30 +12101,30 @@ postcss-image-set-function@^7.0.0: "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -postcss-lab-function@^7.0.6: - version "7.0.6" - resolved "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.6.tgz" - integrity sha512-HPwvsoK7C949vBZ+eMyvH2cQeMr3UREoHvbtra76/UhDuiViZH6pir+z71UaJQohd7VDSVUdR6TkWYKExEc9aQ== +postcss-lab-function@^7.0.12: + version "7.0.12" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-7.0.12.tgz#eb555ac542607730eb0a87555074e4a5c6eef6e4" + integrity sha512-tUcyRk1ZTPec3OuKFsqtRzW2Go5lehW29XA21lZ65XmzQkz43VY2tyWEC202F7W3mILOjw0voOiuxRGTsN+J9w== dependencies: - "@csstools/css-color-parser" "^3.0.6" - "@csstools/css-parser-algorithms" "^3.0.4" - "@csstools/css-tokenizer" "^3.0.3" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" + "@csstools/css-color-parser" "^3.1.0" + "@csstools/css-parser-algorithms" "^3.0.5" + "@csstools/css-tokenizer" "^3.0.4" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" "@csstools/utilities" "^2.0.0" -postcss-loader@^7.3.3: +postcss-loader@^7.3.4: version "7.3.4" - resolved "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.4.tgz#aed9b79ce4ed7e9e89e56199d25ad1ec8f606209" integrity sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A== dependencies: cosmiconfig "^8.3.5" jiti "^1.20.0" semver "^7.5.4" -postcss-logical@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.0.0.tgz" - integrity sha512-HpIdsdieClTjXLOyYdUPAX/XQASNIwdKt5hoZW08ZOAiI+tbV0ta1oclkpVkW5ANU+xJvk3KkA0FejkjGLXUkg== +postcss-logical@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-8.1.0.tgz#4092b16b49e3ecda70c4d8945257da403d167228" + integrity sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA== dependencies: postcss-value-parser "^4.2.0" @@ -12240,12 +12214,12 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-nesting@^13.0.1: - version "13.0.1" - resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz" - integrity sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ== +postcss-nesting@^13.0.2: + version "13.0.2" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-13.0.2.tgz#fde0d4df772b76d03b52eccc84372e8d1ca1402e" + integrity sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ== dependencies: - "@csstools/selector-resolve-nested" "^3.0.0" + "@csstools/selector-resolve-nested" "^3.1.0" "@csstools/selector-specificity" "^5.0.0" postcss-selector-parser "^7.0.0" @@ -12343,67 +12317,73 @@ postcss-place@^10.0.0: dependencies: postcss-value-parser "^4.2.0" -postcss-preset-env@^10.1.0: - version "10.1.1" - resolved "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.1.1.tgz" - integrity sha512-wqqsnBFD6VIwcHHRbhjTOcOi4qRVlB26RwSr0ordPj7OubRRxdWebv/aLjKLRR8zkZrbxZyuus03nOIgC5elMQ== - dependencies: - "@csstools/postcss-cascade-layers" "^5.0.1" - "@csstools/postcss-color-function" "^4.0.6" - "@csstools/postcss-color-mix-function" "^3.0.6" - "@csstools/postcss-content-alt-text" "^2.0.4" - "@csstools/postcss-exponential-functions" "^2.0.5" +postcss-preset-env@^10.2.1: + version "10.5.0" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-10.5.0.tgz#fa9af5b635c053f6d68f91132eab3a0b5c4e53c8" + integrity sha512-xgxFQPAPxeWmsgy8cR7GM1PGAL/smA5E9qU7K//D4vucS01es3M0fDujhDJn3kY8Ip7/vVYcecbe1yY+vBo3qQ== + dependencies: + "@csstools/postcss-alpha-function" "^1.0.1" + "@csstools/postcss-cascade-layers" "^5.0.2" + "@csstools/postcss-color-function" "^4.0.12" + "@csstools/postcss-color-function-display-p3-linear" "^1.0.1" + "@csstools/postcss-color-mix-function" "^3.0.12" + "@csstools/postcss-color-mix-variadic-function-arguments" "^1.0.2" + "@csstools/postcss-content-alt-text" "^2.0.8" + "@csstools/postcss-contrast-color-function" "^2.0.12" + "@csstools/postcss-exponential-functions" "^2.0.9" "@csstools/postcss-font-format-keywords" "^4.0.0" - "@csstools/postcss-gamut-mapping" "^2.0.6" - "@csstools/postcss-gradients-interpolation-method" "^5.0.6" - "@csstools/postcss-hwb-function" "^4.0.6" - "@csstools/postcss-ic-unit" "^4.0.0" - "@csstools/postcss-initial" "^2.0.0" - "@csstools/postcss-is-pseudo-class" "^5.0.1" - "@csstools/postcss-light-dark-function" "^2.0.7" + "@csstools/postcss-gamut-mapping" "^2.0.11" + "@csstools/postcss-gradients-interpolation-method" "^5.0.12" + "@csstools/postcss-hwb-function" "^4.0.12" + "@csstools/postcss-ic-unit" "^4.0.4" + "@csstools/postcss-initial" "^2.0.1" + "@csstools/postcss-is-pseudo-class" "^5.0.3" + "@csstools/postcss-light-dark-function" "^2.0.11" "@csstools/postcss-logical-float-and-clear" "^3.0.0" "@csstools/postcss-logical-overflow" "^2.0.0" "@csstools/postcss-logical-overscroll-behavior" "^2.0.0" "@csstools/postcss-logical-resize" "^3.0.0" - "@csstools/postcss-logical-viewport-units" "^3.0.3" - "@csstools/postcss-media-minmax" "^2.0.5" - "@csstools/postcss-media-queries-aspect-ratio-number-values" "^3.0.4" + "@csstools/postcss-logical-viewport-units" "^3.0.4" + "@csstools/postcss-media-minmax" "^2.0.9" + "@csstools/postcss-media-queries-aspect-ratio-number-values" "^3.0.5" "@csstools/postcss-nested-calc" "^4.0.0" "@csstools/postcss-normalize-display-values" "^4.0.0" - "@csstools/postcss-oklab-function" "^4.0.6" - "@csstools/postcss-progressive-custom-properties" "^4.0.0" - "@csstools/postcss-random-function" "^1.0.1" - "@csstools/postcss-relative-color-syntax" "^3.0.6" + "@csstools/postcss-oklab-function" "^4.0.12" + "@csstools/postcss-position-area-property" "^1.0.0" + "@csstools/postcss-progressive-custom-properties" "^4.2.1" + "@csstools/postcss-random-function" "^2.0.1" + "@csstools/postcss-relative-color-syntax" "^3.0.12" "@csstools/postcss-scope-pseudo-class" "^4.0.1" - "@csstools/postcss-sign-functions" "^1.1.0" - "@csstools/postcss-stepped-value-functions" "^4.0.5" - "@csstools/postcss-text-decoration-shorthand" "^4.0.1" - "@csstools/postcss-trigonometric-functions" "^4.0.5" + "@csstools/postcss-sign-functions" "^1.1.4" + "@csstools/postcss-stepped-value-functions" "^4.0.9" + "@csstools/postcss-system-ui-font-family" "^1.0.0" + "@csstools/postcss-text-decoration-shorthand" "^4.0.3" + "@csstools/postcss-trigonometric-functions" "^4.0.9" "@csstools/postcss-unset-value" "^4.0.0" - autoprefixer "^10.4.19" - browserslist "^4.23.1" + autoprefixer "^10.4.22" + browserslist "^4.28.0" css-blank-pseudo "^7.0.1" - css-has-pseudo "^7.0.1" + css-has-pseudo "^7.0.3" css-prefers-color-scheme "^10.0.0" - cssdb "^8.2.1" + cssdb "^8.5.2" postcss-attribute-case-insensitive "^7.0.1" postcss-clamp "^4.1.0" - postcss-color-functional-notation "^7.0.6" + postcss-color-functional-notation "^7.0.12" postcss-color-hex-alpha "^10.0.0" postcss-color-rebeccapurple "^10.0.0" - postcss-custom-media "^11.0.5" - postcss-custom-properties "^14.0.4" - postcss-custom-selectors "^8.0.4" + postcss-custom-media "^11.0.6" + postcss-custom-properties "^14.0.6" + postcss-custom-selectors "^8.0.5" postcss-dir-pseudo-class "^9.0.1" - postcss-double-position-gradients "^6.0.0" + postcss-double-position-gradients "^6.0.4" postcss-focus-visible "^10.0.1" postcss-focus-within "^9.0.1" postcss-font-variant "^5.0.0" postcss-gap-properties "^6.0.0" postcss-image-set-function "^7.0.0" - postcss-lab-function "^7.0.6" - postcss-logical "^8.0.0" - postcss-nesting "^13.0.1" + postcss-lab-function "^7.0.12" + postcss-logical "^8.1.0" + postcss-nesting "^13.0.2" postcss-opacity-percentage "^3.0.0" postcss-overflow-shorthand "^6.0.0" postcss-page-break "^3.0.4" @@ -12510,7 +12490,7 @@ postcss@8.4.49: picocolors "^1.1.1" source-map-js "^1.2.1" -postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.38: +postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.33: version "8.4.38" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz" integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== @@ -12675,25 +12655,18 @@ pupa@^3.1.0: dependencies: escape-goat "^4.0.0" -qs@6.11.0: - version "6.11.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== +qs@~6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== dependencies: - side-channel "^1.0.4" + side-channel "^1.1.0" queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -queue@6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" - integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== - dependencies: - inherits "~2.0.3" - quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" @@ -12716,15 +12689,15 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== +raw-body@~2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" + integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + unpipe "~1.0.0" rc@1.2.8: version "1.2.8" @@ -12736,36 +12709,6 @@ rc@1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dev-utils@^12.0.1: - version "12.0.1" - resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz" - integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ== - dependencies: - "@babel/code-frame" "^7.16.0" - address "^1.1.2" - browserslist "^4.18.1" - chalk "^4.1.2" - cross-spawn "^7.0.3" - detect-port-alt "^1.1.6" - escape-string-regexp "^4.0.0" - filesize "^8.0.6" - find-up "^5.0.0" - fork-ts-checker-webpack-plugin "^6.5.0" - global-modules "^2.0.0" - globby "^11.0.4" - gzip-size "^6.0.0" - immer "^9.0.7" - is-root "^2.1.0" - loader-utils "^3.2.0" - open "^8.4.0" - pkg-up "^3.1.0" - prompts "^2.4.2" - react-error-overlay "^6.0.11" - recursive-readdir "^2.2.2" - shell-quote "^1.7.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - react-dom@^18.2.0: version "18.3.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" @@ -12781,36 +12724,11 @@ react-error-boundary@^6.0.0: dependencies: "@babel/runtime" "^7.12.5" -react-error-overlay@^6.0.11: - version "6.0.11" - resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz" - integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== - -react-fast-compare@^3.2.0, react-fast-compare@^3.2.2: +react-fast-compare@^3.2.0: version "3.2.2" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz" integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== -react-helmet-async@*: - version "2.0.4" - resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.4.tgz" - integrity sha512-yxjQMWposw+akRfvpl5+8xejl4JtUlHnEBcji6u8/e6oc7ozT+P9PNTWMhCbz2y9tc5zPegw2BvKjQA+NwdEjQ== - dependencies: - invariant "^2.2.4" - react-fast-compare "^3.2.2" - shallowequal "^1.1.0" - -react-helmet-async@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz" - integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== - dependencies: - "@babel/runtime" "^7.12.5" - invariant "^2.2.4" - prop-types "^15.7.2" - react-fast-compare "^3.2.0" - shallowequal "^1.1.0" - "react-helmet-async@npm:@slorber/react-helmet-async@1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz#11fbc6094605cf60aa04a28c17e0aab894b4ecff" @@ -12832,10 +12750,10 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-json-view-lite@^1.2.0: - version "1.4.0" - resolved "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz" - integrity sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA== +react-json-view-lite@^2.3.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/react-json-view-lite/-/react-json-view-lite-2.5.0.tgz#c7ff011c7cc80e9900abc7aa4916c6a5c6d6c1c6" + integrity sha512-tk7o7QG9oYyELWHL8xiMQ8x4WzjCzbWNyig3uexmkLb54r8jO0yH3WCWx8UZS0c49eSA4QUmG5caiRJ8fAn58g== react-live@^4.1.6: version "4.1.6" @@ -13016,25 +12934,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -reading-time@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz" - integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" - integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== - dependencies: - resolve "^1.1.6" - -recursive-readdir@^2.2.2: - version "2.2.3" - resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz" - integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== - dependencies: - minimatch "^3.0.5" - refractor@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz" @@ -13344,7 +13243,7 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@^1.1.6, resolve@^1.14.2: +resolve@^1.14.2: version "1.22.8" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -13377,13 +13276,6 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - robust-predicates@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz" @@ -13399,11 +13291,6 @@ roughjs@^4.6.6: points-on-curve "^0.2.0" points-on-path "^0.2.1" -rtl-detect@^1.0.4: - version "1.1.2" - resolved "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz" - integrity sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ== - rtlcss@^4.1.0: version "4.1.1" resolved "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz" @@ -13414,6 +13301,11 @@ rtlcss@^4.1.0: postcss "^8.4.21" strip-json-comments "^3.1.1" +run-applescript@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.1.0.tgz#2e9e54c4664ec3106c5b5630e249d3d6595c4911" + integrity sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q== + run-con@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/run-con/-/run-con-1.3.2.tgz#755860a10ce326a96b509485fcea50b4d03754e8" @@ -13490,14 +13382,10 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" +schema-dts@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/schema-dts/-/schema-dts-1.1.5.tgz#9237725d305bac3469f02b292a035107595dc324" + integrity sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg== schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" @@ -13518,6 +13406,16 @@ schema-utils@^4.0.0, schema-utils@^4.0.1: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +schema-utils@^4.2.0: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + scrypt-js@3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" @@ -13536,9 +13434,9 @@ select-hose@^2.0.0: resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.1.1: +selfsigned@^2.4.1: version "2.4.1" - resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== dependencies: "@types/node-forge" "^1.3.0" @@ -13561,24 +13459,24 @@ semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4, semver@^7.6.0: resolved "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== -send@0.18.0: - version "0.18.0" - resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== +send@~0.19.0, send@~0.19.1: + version "0.19.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.2.tgz#59bc0da1b4ea7ad42736fd642b1c4294e114ff29" + integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg== dependencies: debug "2.6.9" depd "2.0.0" destroy "1.2.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" + fresh "~0.5.2" + http-errors "~2.0.1" mime "1.6.0" ms "2.1.3" - on-finished "2.4.1" + on-finished "~2.4.1" range-parser "~1.2.1" - statuses "2.0.1" + statuses "~2.0.2" serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.2" @@ -13613,15 +13511,15 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@~1.16.2: + version "1.16.3" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.3.tgz#a97b74d955778583f3862a4f0b841eb4d5d78cf9" + integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA== dependencies: - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.18.0" + send "~0.19.1" set-function-length@^1.2.1: version "1.2.2" @@ -13640,7 +13538,7 @@ setprototypeof@1.1.0: resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.2.0: +setprototypeof@1.2.0, setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -13669,29 +13567,50 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.3, shell-quote@^1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - -shelljs@^0.8.5: - version "0.8.5" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" +shell-quote@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" + integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== side-channel@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: - call-bind "^1.0.7" es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" @@ -13857,6 +13776,11 @@ statuses@2.0.1: resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@~2.0.1, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + std-env@^3.7.0: version "3.8.0" resolved "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz" @@ -14003,16 +13927,11 @@ stylis@4.3.2: resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz" integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg== -stylis@^4.1.3: +stylis@^4.1.3, stylis@^4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== -stylis@^4.3.1: - version "4.3.4" - resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz" - integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== - sucrase@^3.31.0: version "3.35.0" resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz" @@ -14070,6 +13989,14 @@ svgo@^3.0.2, svgo@^3.2.0: csso "^5.0.5" picocolors "^1.0.0" +swr@^2.2.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.8.tgz#aa15596321a34e575226a60576bade0b57adf7bf" + integrity sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w== + dependencies: + dequal "^2.0.3" + use-sync-external-store "^1.6.0" + tabbable@^6.0.0: version "6.2.0" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" @@ -14080,11 +14007,6 @@ tailwind-merge@2.6.0: resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.6.0.tgz#ac5fb7e227910c038d458f396b7400d93a3142d5" integrity sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA== -tapable@^1.0.0: - version "1.1.3" - resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" @@ -14111,11 +14033,6 @@ terser@^5.10.0, terser@^5.15.1, terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" @@ -14130,6 +14047,16 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +thingies@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-2.5.0.tgz#5f7b882c933b85989f8466b528a6247a6881e04f" + integrity sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw== + +throttleit@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" + integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" @@ -14169,9 +14096,9 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.1: +toidentifier@~1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== totalist@^3.0.0: @@ -14184,6 +14111,11 @@ tr46@~0.0.3: resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +tree-dump@^1.0.3, tree-dump@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.1.0.tgz#ab29129169dc46004414f5a9d4a3c6e89f13e8a4" + integrity sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA== + trim-lines@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz" @@ -14302,10 +14234,10 @@ uc.micro@^2.0.0, uc.micro@^2.1.0: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== -ufo@^1.5.4: - version "1.5.4" - resolved "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz" - integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== +ufo@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.1.tgz#ac2db1d54614d1b22c1d603e3aef44a85d8f146b" + integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== undici-types@~5.26.4: version "5.26.5" @@ -14450,7 +14382,7 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -14471,6 +14403,14 @@ update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.0" +update-browserslist-db@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-notifier@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz" @@ -14549,6 +14489,11 @@ use-sync-external-store@^1.4.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== +use-sync-external-store@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -14576,12 +14521,17 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" + integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0, uuid@^9.0.1: +uuid@^9.0.0: version "9.0.1" resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -14724,52 +14674,51 @@ webpack-bundle-analyzer@^4.10.2: sirv "^2.0.3" ws "^7.3.1" -webpack-dev-middleware@^5.3.4: - version "5.3.4" - resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz" - integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== +webpack-dev-middleware@^7.4.2: + version "7.4.5" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz#d4e8720aa29cb03bc158084a94edb4594e3b7ac0" + integrity sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA== dependencies: colorette "^2.0.10" - memfs "^3.4.3" - mime-types "^2.1.31" + memfs "^4.43.1" + mime-types "^3.0.1" + on-finished "^2.4.1" range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.15.2: - version "4.15.2" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz" - integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== - dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.5" +webpack-dev-server@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.2.2.tgz#96a143d50c58fef0c79107e61df911728d7ceb39" + integrity sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/express-serve-static-core" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" + bonjour-service "^1.2.1" + chokidar "^3.6.0" colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^2.0.0" - default-gateway "^6.0.3" - express "^4.17.3" + express "^4.21.2" graceful-fs "^4.2.6" - html-entities "^2.3.2" - http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - launch-editor "^2.6.0" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" + http-proxy-middleware "^2.0.9" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + schema-utils "^4.2.0" + selfsigned "^2.4.1" serve-index "^1.9.1" sockjs "^0.3.24" spdy "^4.0.2" - webpack-dev-middleware "^5.3.4" - ws "^8.13.0" + webpack-dev-middleware "^7.4.2" + ws "^8.18.0" webpack-merge@^5.9.0: version "5.10.0" @@ -14889,13 +14838,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -which@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - which@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" @@ -14967,10 +14909,17 @@ ws@^7.3.1: resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^8.13.0: - version "8.17.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz" - integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== +ws@^8.18.0: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + +wsl-utils@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/wsl-utils/-/wsl-utils-0.1.0.tgz#8783d4df671d4d50365be2ee4c71917a0557baab" + integrity sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw== + dependencies: + is-wsl "^3.1.0" xdg-basedir@^5.0.1, xdg-basedir@^5.1.0: version "5.1.0" @@ -15004,16 +14953,16 @@ yaml@^2.8.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - yocto-queue@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== +zod@^4.1.8: + version "4.2.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.2.1.tgz#07f0388c7edbfd5f5a2466181cb4adf5b5dbd57b" + integrity sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw== + zwitch@^2.0.0: version "2.0.4" resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"