# Asset Requirements (/docs/builderkit/asset-requirements) --- title: Asset Requirements description: "Required assets and file structure for chain and token logos." --- # Asset Requirements BuilderKit requires specific asset files for displaying chain and token logos. These assets should follow a standardized file structure and naming convention. ## Chain Logos Chain logos are used by components like `ChainIcon`, `ChainDropdown`, and `TokenIconWithChain`. ### File Structure Chain logos should be placed at: ``` /chains/logo/{chain_id}.png ``` ### Examples ``` /chains/logo/43114.png // Avalanche C-Chain /chains/logo/43113.png // Fuji Testnet /chains/logo/173750.png // Echo L1 ``` ### Requirements - Format: PNG with transparency - Dimensions: 32x32px (minimum) - Background: Transparent - Shape: Circular or square with rounded corners - File size: < 100KB ## Token Logos Token logos are used by components like `TokenIcon`, `TokenChip`, and `TokenRow`. ### File Structure Token logos should be placed at: ``` /tokens/logo/{chain_id}/{address}.png ``` ### Examples ``` /tokens/logo/43114/0x1234567890123456789012345678901234567890.png // Token on C-Chain /tokens/logo/43113/0x5678901234567890123456789012345678901234.png // Token on Fuji ``` ### Requirements - Format: PNG with transparency - Dimensions: 32x32px (minimum) - Background: Transparent - Shape: Circular or square with rounded corners - File size: < 100KB ## Directory Structure Your public assets directory should look like this: ``` public/ ├── chains/ │ └── logo/ │ ├── 43114.png │ ├── 43113.png │ └── 173750.png └── tokens/ └── logo/ ├── 43114/ │ ├── 0x1234....png │ └── 0x5678....png └── 43113/ ├── 0x9012....png └── 0xabcd....png ``` # Custom Chain Setup (/docs/builderkit/chains) --- title: Custom Chain Setup description: "Configure custom Avalanche L1 chains in your application." --- # Custom Chain Setup Learn how to configure custom Avalanche L1 chains in your BuilderKit application. ## Chain Definition Define your custom L1 chain using `viem`'s `defineChain`: ```tsx import { defineChain } from "viem"; export const myL1 = defineChain({ id: 173750, // Your L1 chain ID name: 'My L1', // Display name network: 'my-l1', // Network identifier nativeCurrency: { decimals: 18, name: 'Token', symbol: 'TKN', }, rpcUrls: { default: { http: ['https://api.avax.network/ext/L1/rpc'] }, }, blockExplorers: { default: { name: 'Explorer', url: 'https://explorer.avax.network/my-l1' }, }, // Optional: Custom metadata iconUrl: "/chains/logo/my-l1.png", icm_registry: "0x..." // ICM registry contract }); ``` ## Provider Configuration Add your custom L1 chain to the Web3Provider: ```tsx import { Web3Provider } from '@avalabs/builderkit'; import { avalanche } from '@wagmi/core/chains'; import { myL1 } from './chains/definitions/my-l1'; function App() { return ( ); } ``` ## Required Properties | Property | Type | Description | |----------|------|-------------| | `id` | `number` | Unique L1 chain identifier | | `name` | `string` | Human-readable chain name | | `network` | `string` | Network identifier | | `nativeCurrency` | `object` | Chain's native token details | | `rpcUrls` | `object` | RPC endpoint configuration | | `blockExplorers` | `object` | Block explorer URLs | ## Optional Properties | Property | Type | Description | |----------|------|-------------| | `iconUrl` | `string` | Chain logo URL | | `icm_registry` | `string` | ICM registry contract address | | `testnet` | `boolean` | Whether the chain is a testnet | ## Example: Echo L1 Here's a complete example using the Echo L1: ```tsx import { defineChain } from "viem"; export const echo = defineChain({ id: 173750, name: 'Echo L1', network: 'echo', nativeCurrency: { decimals: 18, name: 'Ech', symbol: 'ECH', }, rpcUrls: { default: { http: ['https://subnets.avax.network/echo/testnet/rpc'] }, }, blockExplorers: { default: { name: 'Explorer', url: 'https://subnets-test.avax.network/echo' }, }, iconUrl: "/chains/logo/173750.png", icm_registry: "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228" }); ``` # Contribute (/docs/builderkit/contribute) --- title: Contribute description: "Guide for contributing to BuilderKit by building hooks, components, and flows." --- # Contributing to BuilderKit We welcome contributions to BuilderKit! Whether you're fixing bugs, adding new features, or improving documentation, your help makes BuilderKit better for everyone. ## What You Can Contribute ### Hooks Build reusable hooks that handle common Web3 functionality: - Chain data management - Token interactions - Contract integrations - State management - API integrations ### Components Create new UI components or enhance existing ones: - Form elements - Display components - Interactive elements - Layout components - Utility components ### Flows Design complete user journeys by combining components: - Token swaps - NFT minting - Governance voting - Staking interfaces - Custom protocols # Getting Started (/docs/builderkit/getting-started) --- title: Getting Started description: "Quick setup guide for BuilderKit in your React application." --- Get started with BuilderKit in your React application. ## Installation ```bash npm install @avalabs/builderkit # or yarn add @avalabs/builderkit ``` ## Provider Setup Wrap your application with the Web3Provider to enable wallet connections and chain management: ```tsx import { Web3Provider } from '@avalabs/builderkit'; import { avalanche, avalancheFuji } from '@wagmi/core/chains'; import { echo } from './chains/definitions/echo'; import { dispatch } from './chains/definitions/dispatch'; // Configure chains const chains = [avalanche, avalancheFuji, echo, dispatch]; function App() { return ( ); } ``` ## Next Steps - Learn about [Token Configuration](/docs/builderkit/tokens) - Explore [Core Components](/docs/builderkit/components/control) - Check out [Pre-built Flows](/docs/builderkit/flows/ictt) # Introduction (/docs/builderkit) --- title: Introduction description: "A comprehensive React component library for building Web3 applications on Avalanche." --- BuilderKit is a powerful collection of React components and hooks designed specifically for building Web3 applications on Avalanche. It provides everything you need to create modern, user-friendly blockchain applications with minimal effort. ## Ready to Use Components BuilderKit offers a comprehensive set of components that handle common Web3 functionalities: - **Control Components**: Buttons, forms, and wallet connection interfaces - **Identity Components**: Address displays and domain name resolution - **Token Components**: Balance displays, inputs, and price conversions - **Input Components**: Specialized form inputs for Web3 data types - **Chain Components**: Network selection and chain information displays - **Transaction Components**: Transaction submission and status tracking - **Collectibles Components**: NFT displays and collection management ## Powerful Hooks BuilderKit provides hooks for seamless integration with Avalanche's ecosystem: ### Blockchain Interaction Access and manage blockchain data, tokens, and cross-chain operations with hooks for chains, tokens, DEX interactions, and inter-chain transfers. ### Precompile Integration Easily integrate with Avalanche's precompiled contracts for access control, fee management, native minting, rewards, and cross-chain messaging. ## Getting Started Get started quickly by installing BuilderKit in your React application: ```bash npm install @avalabs/builderkit # or yarn add @avalabs/builderkit ``` Check out our [Getting Started](/docs/builderkit/getting-started) guide to begin building your Web3 application. # Token Configuration (/docs/builderkit/tokens) --- title: Token Configuration description: "Guide for configuring tokens in BuilderKit flows." --- # Token Configuration BuilderKit flows require proper token configuration to function correctly. This guide explains the required fields for different token configurations. ## Basic Token Structure All tokens in BuilderKit share a common base structure with these required fields: ```typescript interface BaseToken { // Contract address of the token, use "native" for native chain token address: string; // Human-readable name of the token name: string; // Token symbol/ticker symbol: string; // Number of decimal places the token uses decimals: number; // ID of the chain where this token exists chain_id: number; } ``` ## ICTT Token Fields ICTT tokens extend the base structure with additional fields for cross-chain functionality: ```typescript interface ICTTToken extends BaseToken { // Whether this token can be used with ICTT supports_ictt: boolean; // Address of the contract that handles transfers transferer?: string; // Whether this token instance is a transferer is_transferer?: boolean; // Information about corresponding tokens on other chains mirrors: { // Contract address of the mirrored token address: string; // Transferer contract on the mirror chain transferer: string; // Chain ID where the mirror exists chain_id: number; // Decimal places of the mirrored token decimals: number; // Whether this is the home/original chain home?: boolean; }[]; } ``` ## Field Requirements ### Base Token Fields - `address`: Must be a valid contract address or "native" - `name`: Should be human-readable - `symbol`: Should match the token's trading symbol - `decimals`: Must match the token's contract configuration - `chain_id`: Must be a valid chain ID ### ICTT-Specific Fields - `supports_ictt`: Required for ICTT functionality - `transferer`: Required if token supports ICTT - `is_transferer`: Optional, indicates if token is a transferer - `mirrors`: Required for ICTT, must contain at least one mirror configuration ### Mirror Configuration Fields - `address`: Required, contract address on mirror chain - `transferer`: Required, transferer contract on mirror chain - `chain_id`: Required, must be different from token's chain_id - `decimals`: Required, must match token contract - `home`: Optional, indicates original/home chain # Avalanche L1s (/docs/avalanche-l1s) --- title: Avalanche L1s description: Explore the multi-chain architecture of Avalanche ecosystem. --- An Avalanche L1 is a sovereign network which defines its own rules regarding its membership and token economics. It is composed of a dynamic subset of Avalanche validators working together to achieve consensus on the state of one or more blockchains. Each blockchain is validated by exactly one Avalanche L1, while an Avalanche L1 can validate many blockchains. Avalanche's [Primary Network](/docs/primary-network) is a special Avalanche L1 running three blockchains: - The Platform Chain [(P-Chain)](/docs/primary-network#p-chain-platform-chain) - The Contract Chain [(C-Chain)](/docs/primary-network#c-chain-contract-chain) - The Exchange Chain [(X-Chain)](/docs/primary-network#x-chain-exchange-chain) ![image](/images/subnet1.png) Every validator of an Avalanche L1 **must** sync the P-Chain of the Primary Network for interoperability. Node operators that validate an Avalanche L1 with multiple chains do not need to run multiple machines for validation. For example, the Primary Network is an Avalanche L1 with three coexisting chains, all of which can be validated by a single node, or a single machine. ## Advantages ### Independent Networks - Avalanche L1s use virtual machines to specify their own execution logic, determine their own fee regime, maintain their own state, facilitate their own networking, and provide their own security. - Each Avalanche L1's performance is isolated from other Avalanche L1s in the ecosystem, so increased usage on one Avalanche L1 won't affect another. - Avalanche L1s can have their own token economics with their own native tokens, fee markets, and incentives determined by the Avalanche L1 deployer. - One Avalanche L1 can host multiple blockchains with customized [virtual machines](/docs/primary-network/virtual-machines). ### Native Interoperability Avalanche Warp Messaging enables native cross-Avalanche L1 communication and allows Virtual Machine (VM) developers to implement arbitrary communication protocols between any two Avalanche L1s. ### Accommodate App-Specific Requirements Different blockchain-based applications may require validators to have certain properties such as large amounts of RAM or CPU power. an Avalanche L1 could require that validators meet certain [hardware requirements](/docs/nodes/system-requirements#hardware-and-operating-systems) so that the application doesn't suffer from low performance due to slow validators. ### Launch Networks Designed With Compliance Avalanche's L1 architecture makes regulatory compliance manageable. As mentioned above, an Avalanche L1 may require validators to meet a set of requirements. Some examples of requirements the creators of an Avalanche L1 may choose include: - Validators must be located in a given country. - Validators must pass KYC/AML checks. - Validators must hold a certain license. ### Control Privacy of On-Chain Data Avalanche L1s are ideal for organizations interested in keeping their information private. Institutions conscious of their stakeholders' privacy can create a private Avalanche L1 where the contents of the blockchains would be visible only to a set of pre-approved validators. Define this at creation with a [single parameter](/docs/nodes/configure/avalanche-l1-configs#private-avalanche-l1). ### Validator Sovereignty In a heterogeneous network of blockchains, some validators will not want to validate certain blockchains because they simply have no interest in those blockchains. The Avalanche L1 model enables validators to concern themselves only with blockchain networks they choose to participate in. This greatly reduces the computational burden on validators. ## Why Build Your Own Avalanche L1 There are many advantages to running your own Avalanche L1. If you find one or more of these a good match for your project then an Avalanche L1 might be a good solution for you. ### We Want Our Own Gas Token C-Chain is an Ethereum Virtual Machine (EVM) chain; it requires the gas fees to be paid in its native token. That is, the application may create its own utility tokens (ERC-20) on the C-Chain, but the gas must be paid in AVAX. In the meantime, [Subnet-EVM](https://github.com/ava-labs/subnet-evm) effectively creates an application-specific EVM-chain with full control over native(gas) coins. The operator can pre-allocate the native tokens in the chain genesis, and mint more using the [Subnet-EVM](https://github.com/ava-labs/subnet-evm) precompile contract. And these fees can be either burned (as AVAX burns in C-Chain) or configured to be sent to an address which can be a smart contract. Note that the Avalanche L1 gas token is specific to the application in the chain, thus unknown to the external parties. Moving assets to other chains requires trusted bridge contracts (or upcoming cross Avalanche L1 communication feature). ### We Want Higher Throughput The primary goal of the gas limit on C-Chain is to restrict the block size and therefore prevent network saturation. If a block can be arbitrarily large, it takes longer to propagate, potentially degrading the network performance. The C-Chain gas limit acts as a deterrent against any system abuse but can be quite limiting for high throughput applications. Unlike C-Chain, Avalanche L1 can be single-tenant, dedicated to the specific application, and thus host its own set of validators with higher bandwidth requirements, which allows for a higher gas limit thus higher transaction throughput. Plus, [Subnet-EVM](https://github.com/ava-labs/subnet-evm) supports fee configuration upgrades that can be adaptive to the surge in application traffic. Avalanche L1 workloads are isolated from the Primary Network; which means, the noisy neighbor effect of one workload (for example NFT mint on C-Chain) cannot destabilize the Avalanche L1 or surge its gas price. This failure isolation model in the Avalanche L1 can provide higher application reliability. ### We Want Strict Access Control The C-Chain is open and permissionless where anyone can deploy and interact with contracts. However, for regulatory reasons, some applications may need a consistent access control mechanism for all on-chain transactions. With [Subnet-EVM](https://github.com/ava-labs/subnet-evm), an application can require that "only authorized users may deploy contracts or make transactions." Allow-lists are only updated by the administrators, and the allow list itself is implemented within the precompile contract, thus more transparent and auditable for compliance matters. ### We Need EVM Customization If your project is deployed on the C-Chain then your execution environment is dictated by the setup of the C-Chain. Changing any of the execution parameters means that the configuration of the C-Chain would need to change, and that is expensive, complex and difficult to change. So if your project needs some other capabilities, different execution parameters or precompiles that C-Chain does not provide, then Avalanche L1s are a solution you need. You can configure the EVM in an Avalanche L1 to run however you want, adding precompiles, and setting runtime parameters to whatever your project needs. ### We Need Custom Validator Management With the Etna upgrade, L1s can implement their own validator management logic through a _ValidatorManager_ smart contract. This gives you complete control over your validator set, allowing you to define custom staking rules, implement permissionless proof-of-stake with your own token, or create permissioned proof-of-authority networks. The validator management can be handled directly through smart contracts, giving you programmatic control over validator selection and rewards distribution. ### We Want to Build a Sovereign Network L1s on Avalanche are truly sovereign networks that operate independently without relying on other systems. You have complete control over your network's consensus mechanisms, transaction processing, and security protocols. This independence allows you to scale horizontally without dependencies on other networks while maintaining full control over your network parameters and upgrades. This sovereignty is particularly important for projects that need complete autonomy over their blockchain's operation and evolution. ## When to Choose an Avalanche L1 Here we presented some considerations in favor of running your own Avalanche L1 vs. deploying on the C-Chain. If an application has relatively low transaction rate and no special circumstances that would make the C-Chain a non-starter, you can begin with C-Chain deployment to leverage existing technical infrastructure, and later expand to an Avalanche L1. That way you can focus on working on the core of your project and once you have a solid product/market fit and have gained enough traction that the C-Chain is constricting you, plan a move to your own Avalanche L1. Of course, we're happy to talk to you about your architecture and help you choose the best path forward. Feel free to reach out to us on [Discord](https://chat.avalabs.org/) or other [community channels](https://www.avax.network/community) we run. ## Develop Your Own Avalanche L1 Avalanche L1s on Avalanche are deployed by default with [Subnet-EVM](https://github.com/ava-labs/subnet-evm#subnet-evm), a fork of go-ethereum. It implements the Ethereum Virtual Machine and supports Solidity smart contracts as well as most other Ethereum client functionality. To get started, check out our [L1 Toolbox](/tools/l1-toolbox) or the tutorials in the [Avalanche CLI](/docs/tooling/avalanche-cli) section. # Simple VM in Any Language (/docs/avalanche-l1s/simple-vm-any-language) --- title: Simple VM in Any Language description: Learn how to implement a simple virtual machine in any language. --- This is a language-agnostic high-level documentation explaining the basics of how to get started at implementing your own virtual machine from scratch. Avalanche virtual machines are grpc servers implementing Avalanche's [Proto interfaces](https://buf.build/ava-labs/avalanche). This means that it can be done in [any language that has a grpc implementation](https://grpc.io/docs/languages/). ## Minimal Implementation To get the process started, at the minimum, you will to implement the following interfaces: - [`vm.Runtime`](https://buf.build/ava-labs/avalanche/docs/main:vm.runtime) (Client) - [`vm.VM`](https://buf.build/ava-labs/avalanche/docs/main:vm) (Server) To build a blockchain taking advantage of AvalancheGo's consensus to build blocks, you will need to implement: - [AppSender](https://buf.build/ava-labs/avalanche/docs/main:appsender) (Client) - [Messenger](https://buf.build/ava-labs/avalanche/docs/main:messenger) (Client) To have a json-RPC endpoint, `/ext/bc/subnetId/rpc` exposed by AvalancheGo, you will need to implement: - [`Http`](https://buf.build/ava-labs/avalanche/docs/main:http) (Server) You can and should use a tool like `buf` to generate the (Client/Server) code from the interfaces as stated in the [Avalanche module](https://buf.build/ava-labs/avalanche)'s page. There are _server_ and _client_ interfaces to implement. AvalancheGo calls the _server_ interfaces exposed by your VM and your VM calls the _client_ interfaces exposed by AvalancheGo. ## Starting Process Your VM is started by AvalancheGo launching your binary. Your binary is started as a sub-process of AvalancheGo. While launching your binary, AvalancheGo passes an environment variable `AVALANCHE_VM_RUNTIME_ENGINE_ADDR` containing an url. We must use this url to initialize a `vm.Runtime` client. Your VM, after having started a grpc server implementing the VM interface must call the [`vm.Runtime.InitializeRequest`](https://buf.build/ava-labs/avalanche/docs/main:vm.runtime#vm.runtime.InitializeRequest) with the following parameters. - `protocolVersion`: It must match the `supported plugin version` of the [AvalancheGo release](https://github.com/ava-labs/AvalancheGo/releases) you are using. It is always part of the release notes. - `addr`: It is your grpc server's address. It must be in the following format `host:port` (example `localhost:12345`) ## VM Initialization The service methods are described in the same order as they are called. You will need to implement these methods in your server. ### Pre-Initialization Sequence AvalancheGo starts/stops your process multiple times before launching the real initialization sequence. 1. [VM.Version](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.Version) - Return: your VM's version. 2. [VM.CreateStaticHandler](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.CreateStaticHandlers) - Return: an empty array - (Not absolutely required). 3. [VM.Shutdown](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.Shutdown) - You should gracefully stop your process. - Return: Empty ### Initialization Sequence 1. [VM.CreateStaticHandlers](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.CreateStaticHandlers) - Return an empty array - (Not absolutely required). 2. [VM.Initialize](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.Initialize) - Param: an [InitializeRequest](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.InitializeRequest). - You must use this data to initialize your VM. - You should add the genesis block to your blockchain and set it as the last accepted block. - Return: an [InitializeResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.InitializeResponse) containing data about the genesis extracted from the `genesis_bytes` that was sent in the request. 3. [VM.VerifyHeightIndex](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.VerifyHeightIndex) - Return: a [VerifyHeightIndexResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VerifyHeightIndexResponse) with the code `ERROR_UNSPECIFIED` to indicate that no error has occurred. 4. [VM.CreateHandlers](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.CreateHandlers) - To serve json-RPC endpoint, `/ext/bc/subnetId/rpc` exposed by AvalancheGo - See [json-RPC](#json-rpc) for more detail - Create a [`Http`](https://buf.build/ava-labs/avalanche/docs/main:http) server and get its url. - Return: a `CreateHandlersResponse` containing a single item with the server's url. (or an empty array if not implementing the json-RPC endpoint) 5. [VM.StateSyncEnabled](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.StateSyncEnabled) - Return: `true` if you want to enable StateSync, `false` otherwise. 6. [VM.SetState](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.SetState) _If you had specified `true` in the `StateSyncEnabled` result_ - Param: a [SetStateRequest](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.SetStateRequest) with the `StateSyncing` value - Set your blockchain's state to `StateSyncing` - Return: a [SetStateResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.SetStateResponse) built from the genesis block. 7. [VM.GetOngoingSyncStateSummary](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.GetOngoingSyncStateSummary) _If you had specified `true` in the `StateSyncEnabled` result_ - Return: a [GetOngoingSyncStateSummaryResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.GetOngoingSyncStateSummaryResponse) built from the genesis block. 8. [VM.SetState](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.SetState) - Param: a [SetStateRequest](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.SetStateRequest) with the `Bootstrapping` value - Set your blockchain's state to `Bootstrapping` - Return: a [SetStateResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.SetStateResponse) built from the genesis block. 9. [VM.SetPreference](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.SetPreference) - Param: `SetPreferenceRequest` containing the preferred block ID - Return: Empty 10. [VM.SetState](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.SetState) - Param: a [SetStateRequest](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.SetStateRequest) with the `NormalOp` value - Set your blockchain's state to `NormalOp` - Return: a [SetStateResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.SetStateResponse) built from the genesis block. 11. [VM.Connected](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.Connected) (for every other node validating this Avalanche L1 in the network) - Param: a [ConnectedRequest](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.ConnectedRequest) with the NodeID and the version of AvalancheGo. - Return: Empty 12. [VM.Health](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.Health) - Param: Empty - Return: a [HealthResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.HealthResponse) with an empty `details` property. 13. [VM.ParseBlock](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.ParseBlock) - Param: A byte array containing a Block (the genesis block in this case) - Return: a [ParseBlockResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.ParseBlockResponse) built from the last accepted block. At this point, your VM is fully started and initialized. ### Building Blocks #### Transaction Gossiping Sequence When your VM receives transactions (for example using the [json-RPC](#json-rpc) endpoints), it can gossip them to the other nodes by using the [AppSender](https://buf.build/ava-labs/avalanche/docs/main:appsender) service. Supposing we have a 3 nodes network with nodeX, nodeY, nodeZ. Let's say NodeX has received a new transaction on it's json-RPC endpoint. [`AppSender.SendAppGossip`](https://buf.build/ava-labs/avalanche/docs/main:appsender#appsender.AppSender.SendAppGossip) (_client_): You must serialize your transaction data into a byte array and call the `SendAppGossip` to propagate the transaction. AvalancheGo then propagates this to the other nodes. [VM.AppGossip](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.AppGossip): You must deserialize the transaction and store it for the next block. - Param: A byte array containing your transaction data, and the NodeID of the node which sent the gossip message. - Return: Empty #### Block Building Sequence Whenever your VM is ready to build a new block, it will initiate the block building process by using the [Messenger](https://buf.build/ava-labs/avalanche/docs/main:messenger) service. Supposing that nodeY wants to build the block. you probably will implement some kind of background worker checking every second if there are any pending transactions: _client_ [`Messenger.Notify`](https://buf.build/ava-labs/avalanche/docs/main:messenger#messenger.Messenger.Notify): You must issue a notify request to AvalancheGo by calling the method with the `MESSAGE_BUILD_BLOCK` value. 1. [VM.BuildBlock](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.BuildBlock) - Param: Empty - You must build a block with your pending transactions. Serialize it to a byte array. - Store this block in memory as a "pending blocks" - Return: a [BuildBlockResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.BuildBlockResponse) from the newly built block and it's associated data (`id`, `parent_id`, `height`, `timestamp`). 2. [VM.BlockVerify](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.BlockVerify) - Param: The byte array containing the block data - Return: the block's timestamp 3. [VM.SetPreference](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.SetPreference) - Param: The block's ID - You must mark this block as the next preferred block. - Return: Empty 1. [VM.ParseBlock](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.ParseBlock) - Param: A byte array containing a the newly built block's data - Store this block in memory as a "pending blocks" - Return: a [ParseBlockResponse](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.ParseBlockResponse) built from the last accepted block. 2. [VM.BlockVerify](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.BlockVerify) - Param: The byte array containing the block data - Return: the block's timestamp 3. [VM.SetPreference](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.SetPreference) - Param: The block's ID - You must mark this block as the next preferred block. - Return: Empty [VM.BlockAccept](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.BlockAccept): You must accept this block as your last final block. - Param: The block's ID - Return: Empty #### Managing Conflicts Conflicts happen when two or more nodes propose the next block at the same time. AvalancheGo takes care of this and decides which block should be considered final, and which blocks should be rejected using Snowman consensus. On the VM side, all there is to do is implement the `VM.BlockAccept` and `VM.BlockReject` methods. _nodeX proposes block `0x123...`, nodeY proposes block `0x321...` and nodeZ proposes block `0x456`_ There are three conflicting blocks (different hashes), and if we look at our VM's log files, we can see that AvalancheGo uses Snowman to decide which block must be accepted. ```bash ... snowman/voter.go:58 filtering poll results ... ... snowman/voter.go:65 finishing poll ... ... snowman/voter.go:87 Snowman engine can't quiesce ... ... snowman/voter.go:58 filtering poll results ... ... snowman/voter.go:65 finishing poll ... ... snowman/topological.go:600 accepting block ``` Supposing that AvalancheGo accepts block `0x123...`. The following RPC methods are called on all nodes: 1. [VM.BlockAccept](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.BlockAccept): You must accept this block as your last final block. - Param: The block's ID (`0x123...`) - Return: Empty 2. [VM.BlockReject](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.BlockReject): You must mark this block as rejected. - Param: The block's ID (`0x321...`) - Return: Empty 3. [VM.BlockReject](https://buf.build/ava-labs/avalanche/docs/main:vm#vm.VM.BlockReject): You must mark this block as rejected. - Param: The block's ID (`0x456...`) - Return: Empty ### JSON-RPC To enable your json-RPC endpoint, you must implement the [HandleSimple](https://buf.build/ava-labs/avalanche/docs/main:http#http.HTTP.HandleSimple) method of the [`Http`](https://buf.build/ava-labs/avalanche/docs/main:http) interface. - Param: a [HandleSimpleHTTPRequest](https://buf.build/ava-labs/avalanche/docs/main:http#http.HandleSimpleHTTPRequest) containing the original request's method, url, headers, and body. - Analyze, deserialize and handle the request. For example: if the request represents a transaction, we must deserialize it, check the signature, store it and gossip it to the other nodes using the [messenger client](#block-building-sequence)). - Return the [HandleSimpleHTTPResponse](https://buf.build/ava-labs/avalanche/docs/main:http#http.HandleSimpleHTTPResponse) response that will be sent back to the original sender. This server is registered with AvalancheGo during the [initialization process](#initialization-sequence) when the `VM.CreateHandlers` method is called. You must simply respond with the server's url in the `CreateHandlersResponse` result. # Introduction (/docs/avalanche-l1s/virtual-machines-index) --- title: Introduction description: Learn about the execution layer of a blockchain network. --- A Virtual Machine (VM) is a blueprint for a blockchain. Blockchains are instantiated from a VM, similar to how objects are instantiated from a class definition. VMs can define anything you want, but will generally define transactions that are executed and how blocks are created. ## Blocks and State Virtual Machines deal with blocks and state. The functionality provided by VMs is to: - Define the representation of a blockchain's state - Represent the operations in that state - Apply the operations in that state Each block in the blockchain contains a set of state transitions. Each block is applied in order from the blockchain's initial genesis block to its last accepted block to reach the latest state of the blockchain. ## Blockchain A blockchain relies on two major components: The **Consensus Engine** and the **VM**. The VM defines application specific behavior and how blocks are built and parsed to create the blockchain. All VMs run on top of the Avalanche Consensus Engine, which allows nodes in the network to agree on the state of the blockchain. Here's a quick example of how VMs interact with consensus: 1. A node wants to update the blockchain's state 2. The node's VM will notify the consensus engine that it wants to update the state 3. The consensus engine will request the block from the VM 4. The consensus engine will verify the returned block using the VM's implementation of `Verify()` 5. The consensus engine will get the network to reach consensus on whether to accept or reject the newly verified block. Every virtuous (well-behaved) node on the network will have the same preference for a particular block 6. Depending upon the consensus results, the engine will either accept or reject the block. What happens when a block is accepted or rejected is specific to the implementation of the VM AvalancheGo provides the consensus engine for every blockchain on the Avalanche Network. The consensus engine relies on the VM interface to handle building, parsing, and storing blocks as well as verifying and executing on behalf of the consensus engine. This decoupling between the application and consensus layer allows developers to build their applications quickly by implementing virtual machines, without having to worry about the consensus layer managed by Avalanche which deals with how nodes agree on whether or not to accept a block. ## Installing a VM VMs are supplied as binaries to a node running `AvalancheGo`. These binaries must be named the VM's assigned **VMID**. A VMID is a 32-byte hash encoded in CB58 that is generated when you build your VM. In order to install a VM, its binary must be installed in the `AvalancheGo` plugin path. See [here](/docs/nodes/configure/configs-flags#--plugin-dir-string) for more details. Multiple VMs can be installed in this location. Each VM runs as a separate process from AvalancheGo and communicates with `AvalancheGo` using gRPC calls. This functionality is enabled by **RPCChainVM**, a special VM which wraps around other VM implementations and bridges the VM and AvalancheGo, establishing a standardized communication protocol between them. During VM creation, handshake messages are exchanged via **RPCChainVM** between AvalancheGo and the VM installation. Ensure matching **RPCChainVM** protocol versions to avoid errors, by updating your VM or using a [different version of AvalancheGo](https://github.com/ava-labs/AvalancheGo/releases). Note that some VMs may not support the latest protocol version. ### API Handlers Users can interact with a blockchain and its VM through handlers exposed by the VM's API. VMs expose two types of handlers to serve responses for incoming requests: - **Blockchain Handlers**: Referred to as handlers, these expose APIs to interact with a blockchain instantiated by a VM. The API endpoint will be different for each chain. The endpoint for a handler is `/ext/bc/[chainID]`. - **VM Handlers**: Referred to as static handlers, these expose APIs to interact with the VM directly. One example API would be to parse genesis data to instantiate a new blockchain. The endpoint for a static handler is `/ext/vm/[vmID]`. For any readers familiar with object-oriented programming, static and non-static handlers on a VM are analogous to static and non-static methods on a class. Blockchain handlers can be thought of as methods on an object, whereas VM handlers can be thought of as static methods on a class. ### Instantiate a VM The `vm.Factory` interface is implemented to create new VM instances from which a blockchain can be initialized. The factory's `New` method shown below provides `AvalancheGo` with an instance of the VM. It's defined in the [`factory.go`](https://github.com/ava-labs/timestampvm/blob/main/timestampvm/factory.go) file of the `timestampvm` repository. ```go // Returning a new VM instance from VM's factory func (f *Factory) New(*snow.Context) (interface{}, error) { return &vm.VM{}, nil } ``` ### Initializing a VM to Create a Blockchain Before a VM can run, AvalancheGo will initialize it by invoking its `Initialize` method. Here, the VM will bootstrap itself and sets up anything it requires before it starts running. This might involve setting up its database, mempool, genesis state, or anything else the VM requires to run. ```go if err := vm.Initialize( ctx.Context, vmDBManager, genesisData, chainConfig.Upgrade, chainConfig.Config, msgChan, fxs, sender, ); ``` You can refer to the [implementation](https://github.com/ava-labs/timestampvm/blob/main/timestampvm/vm.go#L75) of `vm.initialize` in the TimestampVM repository. ## Interfaces Every VM should implement the following interfaces: ### `block.ChainVM` To reach a consensus on linear blockchains, Avalanche uses the Snowman consensus engine. To be compatible with Snowman, a VM must implement the `block.ChainVM` interface. For more information, see [here](https://github.com/ava-labs/avalanchego/blob/master/snow/engine/snowman/block/vm.go). ```go title="snow/engine/snowman/block/vm.go" // ChainVM defines the required functionality of a Snowman VM. // // A Snowman VM is responsible for defining the representation of the state, // the representation of operations in that state, the application of operations // on that state, and the creation of the operations. Consensus will decide on // if the operation is executed and the order operations are executed. // // For example, suppose we have a VM that tracks an increasing number that // is agreed upon by the network. // The state is a single number. // The operation is setting the number to a new, larger value. // Applying the operation will save to the database the new value. // The VM can attempt to issue a new number, of larger value, at any time. // Consensus will ensure the network agrees on the number at every block height. type ChainVM interface { common.VM Getter Parser // Attempt to create a new block from data contained in the VM. // // If the VM doesn't want to issue a new block, an error should be // returned. BuildBlock() (snowman.Block, error) // Notify the VM of the currently preferred block. // // This should always be a block that has no children known to consensus. SetPreference(ids.ID) error // LastAccepted returns the ID of the last accepted block. // // If no blocks have been accepted by consensus yet, it is assumed there is // a definitionally accepted block, the Genesis block, that will be // returned. LastAccepted() (ids.ID, error) } // Getter defines the functionality for fetching a block by its ID. type Getter interface { // Attempt to load a block. // // If the block does not exist, an error should be returned. // GetBlock(ids.ID) (snowman.Block, error) } // Parser defines the functionality for fetching a block by its bytes. type Parser interface { // Attempt to create a block from a stream of bytes. // // The block should be represented by the full byte array, without extra // bytes. ParseBlock([]byte) (snowman.Block, error) } ``` ### `common.VM` `common.VM` is a type that every `VM` must implement. For more information, you can see the full file [here](https://github.com/ava-labs/avalanchego/blob/master/snow/engine/common/vm.go). ```go title="snow/engine/common/vm.go" // VM describes the interface that all consensus VMs must implement type VM interface { // Contains handlers for VM-to-VM specific messages AppHandler // Returns nil if the VM is healthy. // Periodically called and reported via the node's Health API. health.Checkable // Connector represents a handler that is called on connection connect/disconnect validators.Connector // Initialize this VM. // [ctx]: Metadata about this VM. // [ctx.networkID]: The ID of the network this VM's chain is running on. // [ctx.chainID]: The unique ID of the chain this VM is running on. // [ctx.Log]: Used to log messages // [ctx.NodeID]: The unique staker ID of this node. // [ctx.Lock]: A Read/Write lock shared by this VM and the consensus // engine that manages this VM. The write lock is held // whenever code in the consensus engine calls the VM. // [dbManager]: The manager of the database this VM will persist data to. // [genesisBytes]: The byte-encoding of the genesis information of this // VM. The VM uses it to initialize its state. For // example, if this VM were an account-based payments // system, `genesisBytes` would probably contain a genesis // transaction that gives coins to some accounts, and this // transaction would be in the genesis block. // [toEngine]: The channel used to send messages to the consensus engine. // [fxs]: Feature extensions that attach to this VM. Initialize( ctx *snow.Context, dbManager manager.Manager, genesisBytes []byte, upgradeBytes []byte, configBytes []byte, toEngine chan<- Message, fxs []*Fx, appSender AppSender, ) error // Bootstrapping is called when the node is starting to bootstrap this chain. Bootstrapping() error // Bootstrapped is called when the node is done bootstrapping this chain. Bootstrapped() error // Shutdown is called when the node is shutting down. Shutdown() error // Version returns the version of the VM this node is running. Version() (string, error) // Creates the HTTP handlers for custom VM network calls. // // This exposes handlers that the outside world can use to communicate with // a static reference to the VM. Each handler has the path: // [Address of node]/ext/VM/[VM ID]/[extension] // // Returns a mapping from [extension]s to HTTP handlers. // // Each extension can specify how locking is managed for convenience. // // For example, it might make sense to have an extension for creating // genesis bytes this VM can interpret. CreateStaticHandlers() (map[string]*HTTPHandler, error) // Creates the HTTP handlers for custom chain network calls. // // This exposes handlers that the outside world can use to communicate with // the chain. Each handler has the path: // [Address of node]/ext/bc/[chain ID]/[extension] // // Returns a mapping from [extension]s to HTTP handlers. // // Each extension can specify how locking is managed for convenience. // // For example, if this VM implements an account-based payments system, // it have an extension called `accounts`, where clients could get // information about their accounts. CreateHandlers() (map[string]*HTTPHandler, error) } ``` ### `snowman.Block` The `snowman.Block` interface It define the functionality a block must implement to be a block in a linear Snowman chain. For more information, you can see the full file [here](https://github.com/ava-labs/avalanchego/blob/master/snow/consensus/snowman/block.go). ```go title="snow/consensus/snowman/block.go" // Block is a possible decision that dictates the next canonical block. // // Blocks are guaranteed to be Verified, Accepted, and Rejected in topological // order. Specifically, if Verify is called, then the parent has already been // verified. If Accept is called, then the parent has already been accepted. If // Reject is called, the parent has already been accepted or rejected. // // If the status of the block is Unknown, ID is assumed to be able to be called. // If the status of the block is Accepted or Rejected; Parent, Verify, Accept, // and Reject will never be called. type Block interface { choices.Decidable // Parent returns the ID of this block's parent. Parent() ids.ID // Verify that the state transition this block would make if accepted is // valid. If the state transition is invalid, a non-nil error should be // returned. // // It is guaranteed that the Parent has been successfully verified. Verify() error // Bytes returns the binary representation of this block. // // This is used for sending blocks to peers. The bytes should be able to be // parsed into the same block on another node. Bytes() []byte // Height returns the height of this block in the chain. Height() uint64 } ``` ### `choices.Decidable` This interface is a superset of every decidable object, such as transactions, blocks, and vertices. For more information, you can see the full file [here](https://github.com/ava-labs/avalanchego/blob/master/snow/choices/decidable.go). ```go title="snow/choices/decidable.go" // Decidable represents element that can be decided. // // Decidable objects are typically thought of as either transactions, blocks, or // vertices. type Decidable interface { // ID returns a unique ID for this element. // // Typically, this is implemented by using a cryptographic hash of a // binary representation of this element. An element should return the same // IDs upon repeated calls. ID() ids.ID // Accept this element. // // This element will be accepted by every correct node in the network. Accept() error // Reject this element. // // This element will not be accepted by any correct node in the network. Reject() error // Status returns this element's current status. // // If Accept has been called on an element with this ID, Accepted should be // returned. Similarly, if Reject has been called on an element with this // ID, Rejected should be returned. If the contents of this element are // unknown, then Unknown should be returned. Otherwise, Processing should be // returned. Status() Status } ``` # WAGMI Avalanche L1 (/docs/avalanche-l1s/wagmi-avalanche-l1) --- title: WAGMI Avalanche L1 description: Learn about the WAGMI Avalanche L1 in this detailed case study. --- This is one of the first cases of using Avalanche L1s as a proving ground for changes in a production VM (Coreth). Many underestimate how useful the isolation of Avalanche L1s is for performing complex VM testing on a live network (without impacting the stability of the primary network). We created a basic WAGMI Explorer [https://subnets-test.avax.network/wagmi](https://subnets-test.avax.network/wagmi) that surfaces aggregated usage statistics about the Avalanche L1. - SubnetID: [28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY](https://explorer-xp.avax-test.network/avalanche-l1/28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY?tab=validators) - ChainID: [2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt](https://testnet.avascan.info/blockchain/2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt) ### Network Parameters[​](#network-parameters "Direct link to heading") - NetworkID: 11111 - ChainID: 11111 - Block Gas Limit: 20,000,000 (2.5x C-Chain) - 10s Gas Target: 100,000,000 (~6.67x C-Chain) - Min Fee: 1 Gwei (4% of C-Chain) - Target Block Rate: 2s (Same as C-Chain) The genesis file of WAGMI can be found [here](https://github.com/ava-labs/public-chain-assets/blob/1951594346dcc91682bdd8929bcf8c1bf6a04c33/chains/11111/genesis.json). ### Adding WAGMI to Core[​](#adding-wagmi-to-core "Direct link to heading") - Network Name: WAGMI - RPC URL: [https://subnets.avax.network/wagmi/wagmi-chain-testnet/rpc] - WS URL: wss://avalanche-l1s.avax.network/wagmi/wagmi-chain-testnet/ws - Chain ID: 11111 - Symbol: WGM - Explorer: [https://subnets.avax.network/wagmi/wagmi-chain-testnet/explorer] This can be used with other wallets too, such as MetaMask. Case Study: WAGMI Upgrades[​](#case-study-wagmi-upgrades "Direct link to heading") ---------------------------------------------------------------------------------- This case study uses [WAGMI](https://subnets-test.avax.network/wagmi) Avalanche L1 upgrade to show how a network upgrade on an EVM-based (Ethereum Virtual Machine) Avalanche L1 can be done simply, and how the resulting upgrade can be used to dynamically control fee structure on the Avalanche L1. ### Introduction[​](#introduction "Direct link to heading") [Subnet-EVM](https://github.com/ava-labs/subnet-evm) aims to provide an easy to use toolbox to customize the EVM for your blockchain. It is meant to run out of the box for many Avalanche L1s without any modification. But what happens when you want to add a new feature updating the rules of your EVM? Instead of hard coding the timing of network upgrades in client code like most EVM chains, requiring coordinated deployments of new code, [Subnet-EVM v0.2.8](https://github.com/ava-labs/subnet-evm/releases/tag/v0.2.8) introduces the long awaited feature to perform network upgrades by just using a few lines of JSON in a configuration file. ### Network Upgrades: Enable/Disable Precompiles[​](#network-upgrades-enabledisable-precompiles "Direct link to heading") Detailed description of how to do this can be found in [Customize an Avalanche L1](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#network-upgrades-enabledisable-precompiles) tutorial. Here's a summary: 1. Network Upgrade utilizes existing precompiles on the Subnet-EVM: - ContractDeployerAllowList, for restricting smart contract deployers - TransactionAllowList, for restricting who can submit transactions - NativeMinter, for minting native coins - FeeManager, for configuring dynamic fees - RewardManager, for enabling block rewards 2. Each of these precompiles can be individually enabled or disabled at a given timestamp as a network upgrade, or any of the parameters governing its behavior changed. 3. These upgrades must be specified in a file named `upgrade.json` placed in the same directory where [`config.json`](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#avalanchego-chain-configs) resides: `{chain-config-dir}/{blockchainID}/upgrade.json`. ### Preparation[​](#preparation "Direct link to heading") To prepare for the first WAGMI network upgrade, on August 15, 2022, we had announced on [X](https://x.com/AaronBuchwald/status/1559249414102720512) and shared on other social media such as Discord. For the second upgrade, on February 24, 2024, we had another announcement on [X](https://x.com/jceyonur/status/1760777031858745701?s=20). ### Deploying upgrade.json[​](#deploying-upgradejson "Direct link to heading") The content of the `upgrade.json` is: ```json { "precompileUpgrades": [ { "feeManagerConfig": { "adminAddresses": ["0x6f0f6DA1852857d7789f68a28bba866671f3880D"], "blockTimestamp": 1660658400 } }, { "contractNativeMinterConfig": { "blockTimestamp": 1708696800, "adminAddresses": ["0x6f0f6DA1852857d7789f68a28bba866671f3880D"], "managerAddresses": ["0xadFA2910DC148674910c07d18DF966A28CD21331"] } } ] } ``` With the above `upgrade.json`, we intend to perform two network upgrades: 1. The first upgrade is to activate the FeeManager precompile: - `0x6f0f6DA1852857d7789f68a28bba866671f3880D` is named as the new Admin of the FeeManager precompile. - `1660658400` is the [Unix timestamp](https://www.unixtimestamp.com/) for Tue Aug 16 2022 14:00:00 GMT+0000 (future time when we made the announcement) when the new FeeManager change would take effect. 2. The second upgrade is to activate the NativeMinter precompile: - `0x6f0f6DA1852857d7789f68a28bba866671f3880D` is named as the new Admin of the NativeMinter precompile. - `0xadFA2910DC148674910c07d18DF966A28CD21331` is named as the new Manager of the NativeMinter precompile. Manager addresses are enabled after Durango upgrades which occurred on February 13, 2024. - `1708696800` is the [Unix timestamp](https://www.unixtimestamp.com/) for Fri Feb 23 2024 14:00:00 GMT+0000 (future time when we made the announcement) when the new NativeMinter change would take effect. Detailed explanations of feeManagerConfig can be found in [here](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#configuring-dynamic-fees), and for the contractNativeMinterConfig in [here](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#minting-native-coins). We place the `upgrade.json` file in the chain config directory, which in our case is `~/.avalanchego/configs/chains/2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt/`. After that, we restart the node so the upgrade file is loaded. When the node restarts, AvalancheGo reads the contents of the JSON file and passes it into Subnet-EVM. We see a log of the chain configuration that includes the updated precompile upgrade. It looks like this: ```bash INFO [02-22|18:27:06.473] <2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt Chain> github.com/ava-labs/subnet-evm/core/blockchain.go:335: Upgrade Config: {"precompileUpgrades":[{"feeManagerConfig":{"adminAddresses":["0x6f0f6da1852857d7789f68a28bba866671f3880d"],"blockTimestamp":1660658400}},{"contractNativeMinterConfig":{"adminAddresses":["0x6f0f6da1852857d7789f68a28bba866671f3880d"],"managerAddresses":["0xadfa2910dc148674910c07d18df966a28cd21331"],"blockTimestamp":1708696800}}]} ``` We note that `precompileUpgrades` correctly shows the upcoming precompile upgrades. Upgrade is locked in and ready. ### Activations[​](#activations "Direct link to heading") When the time passed 10:00 AM EDT August 16, 2022 (Unix timestamp 1660658400), the `upgrade.json` had been executed as planned and the new FeeManager admin address has been activated. From now on, we don't need to issue any new code or deploy anything on the WAGMI nodes to change the fee structure. Let's see how it works in practice! For the second upgrade on February 23, 2024, the same process was followed. The `upgrade.json` had been executed after Durango, as planned, and the new NativeMinter admin and manager addresses have been activated. ### Using Fee Manager[​](#using-fee-manager "Direct link to heading") The owner `0x6f0f6DA1852857d7789f68a28bba866671f3880D` can now configure the fees on the Avalanche L1 as they see fit. To do that, all that's needed is access to the network, the private key for the newly set manager address and making calls on the precompiled contract. We will use [Remix](https://remix.ethereum.org/) online Solidity IDE and the [Core Browser Extension](https://support.avax.network/en/articles/6066879-core-extension-how-do-i-add-the-core-extension). Core comes with WAGMI network built-in. MetaMask will do as well but you will need to [add WAGMI](/docs/avalanche-l1s/wagmi-avalanche-l1) yourself. First using Core, we open the account as the owner `0x6f0f6DA1852857d7789f68a28bba866671f3880D`. Then we connect Core to WAGMI, Switch on the `Testnet Mode` in `Advanced` page in the hamburger menu: ![Core Testnet mode](/images/wagmi1.png) And then open the `Manage Networks` menu in the networks dropdown. Select WAGMI there by clicking the star icon: ![Core network selection](/images/wagmi2.png) We then switch to WAGMI in the networks dropdown. We are ready to move on to Remix now, so we open it in the browser. First, we check that Remix sees the extension and correctly talks to it. We select `Deploy & run transactions` icon on the left edge, and on the Environment dropdown, select `Injected Provider`. We need to approve the Remix network access in the Core browser extension. When that is done, `Custom (11111) network` is shown: ![Injected provider](/images/wagmi3.png) Good, we're talking to WAGMI Avalanche L1. Next we need to load the contracts into Remix. Using 'load from GitHub' option from the Remix home screen we load two contracts: - [IAllowList.sol](https://github.com/ava-labs/subnet-evm/blob/master/contracts/contracts/interfaces/IAllowList.sol) - and [IFeeManager.sol](https://github.com/ava-labs/subnet-evm/blob/master/contracts/contracts/interfaces/IFeeManager.sol). IFeeManager is our precompile, but it references the IAllowList, so we need that one as well. We compile IFeeManager.sol and use deployed contract at the precompile address `0x0200000000000000000000000000000000000003` used on the [Avalanche L1](https://github.com/ava-labs/subnet-evm/blob/master/precompile/contracts/feemanager/module.go#L21). ![Deployed contract](/images/wagmi4.png) Now we can interact with the FeeManager precompile from within Remix via Core. For example, we can use the `getFeeConfig` method to check the current fee configuration. This action can be performed by anyone as it is just a read operation. Once we have the new desired configuration for the fees on the Avalanche L1, we can use the `setFeeConfig` to change the parameters. This action can **only** be performed by the owner `0x6f0f6DA1852857d7789f68a28bba866671f3880D` as the `adminAddress` specified in the [`upgrade.json` above](#deploying-upgradejson). ![setFeeConfig](/images/wagmi5.png) When we call that method by pressing the `transact` button, a new transaction is posted to the Avalanche L1, and we can see it on [the explorer](https://subnets-test.avax.network/wagmi/block/0xad95ccf04f6a8e018ece7912939860553363cc23151a0a31ea429ba6e60ad5a3): ![transaction](/images/wagmi6.png) Immediately after the transaction is accepted, the new fee config takes effect. We can check with the `getFeeCofig` that the values are reflected in the active fee config (again this action can be performed by anyone): ![getFeeConfig](/images/wagmi7.png) That's it, fees changed! No network upgrades, no complex and risky deployments, just making a simple contract call and the new fee configuration is in place! ### Using NativeMinter[​](#using-nativeminter "Direct link to heading") For the NativeMinter, we can use the same process to connect to the Avalanche L1 and interact with the precompile. We can load INativeMinter interface using 'load from GitHub' option from the Remix home screen with following contracts: - [IAllowList.sol](https://github.com/ava-labs/subnet-evm/blob/master/contracts/contracts/interfaces/IAllowList.sol) - and [INativeMinter.sol](https://github.com/ava-labs/subnet-evm/blob/master/contracts/contracts/interfaces/INativeMinter.sol). We can compile them and interact with the deployed contract at the precompile address `0x0200000000000000000000000000000000000001` used on the [Avalanche L1](https://github.com/ava-labs/subnet-evm/blob/master/precompile/contracts/nativeminter/module.go#L22). ![Deployed contract](/images/wagmi8.png) The native minter precompile is used to mint native coins to specified addresses. The minted coins is added to the current supply and can be used by the recipient to pay for gas fees. For more information about the native minter precompile see [here](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#minting-native-coins). `mintNativeCoin` method can be only called by enabled, manager and admin addresses. For this upgrade we have added both an admin and a manager address in [`upgrade.json` above](#deploying-upgradejson). The manager address was available after Durango upgrades which occurred on February 13, 2024. We will use the manager address `0xadfa2910dc148674910c07d18df966a28cd21331` to mint native coins. ![mintNativeCoin](/images/wagmi9.png) When we call that method by pressing the `transact` button, a new transaction is posted to the Avalanche L1, and we can see it on [the explorer](https://subnets-test.avax.network/wagmi/tx/0xc4aaba7b5863c1b8f6664ac1d483e2d7d392ab58d1a8feb0b6c318cbae7f1e93): ![tx](/images/wagmi10.png) As a result of this transaction, the native minter precompile minted a new native coin (1 WGM) to the recipient address `0xB78cbAa319ffBD899951AA30D4320f5818938310`. The address page on the explorer [here](https://subnets-test.avax.network/wagmi/address/0xB78cbAa319ffBD899951AA30D4320f5818938310) shows no incoming transaction; this is because the 1 WGM was directly minted by the EVM itself, without any sender. ### Conclusion[​](#conclusion "Direct link to heading") Network upgrades can be complex and perilous procedures to carry out safely. Our continuing efforts with Avalanche L1s is to make upgrades as painless and simple as possible. With the powerful combination of stateful precompiles and network upgrades via the upgrade configuration files we have managed to greatly simplify both the network upgrades and network parameter changes. This in turn enables much safer experimentation and many new use cases that were too risky and complex to carry out with high-coordination efforts required with the traditional network upgrade mechanisms. We hope this case study will help spark ideas for new things you may try on your own. We're looking forward to seeing what you have built and how easy upgrades help you in managing your Avalanche L1s! If you have any questions or issues, feel free to contact us on our [Discord](https://chat.avalabs.org/). Or just reach out to tell us what exciting new things you have built! # Introduction (/docs/cross-chain) --- title: Introduction description: Learn about different interoperability protocols in the Avalanche ecosystem. --- # Introduction (/docs/nodes) --- title: Introduction description: A brief introduction to the concepts of nodes and validators within the Avalanche ecosystem. --- The Avalanche network is a decentralized platform designed for high throughput and low latency, enabling a wide range of applications. At the core of the network are nodes and validators, which play vital roles in maintaining the network's security, reliability, and performance. ## What is a Node? A node in the Avalanche network is any computer that participates in the network by maintaining a copy of the blockchain, relaying information, and validating transactions. Nodes can be of different types depending on their role and level of participation in the network’s operations. ### Types of Nodes - **Full Node**: Stores the entire blockchain data and helps propagate transactions and blocks across the network. It does not participate directly in consensus but is crucial for the network's health and decentralization. **Archival full nodes** store the entire blockchain ledger, including all transactions from the beginning to the most recent. **Pruned full nodes** download the blockchain ledger, then delete blocks starting with the oldest to save memory. - **Validator Node**: A specialized type of full node that actively participates in the consensus process by validating transactions, producing blocks, and securing the network. Validator nodes are required to stake AVAX tokens as collateral to participate in the consensus mechanism. - **RPC (Remote Procedure Call) Node**: These nodes act as an interface, enabling third-party applications to query and interact with the blockchain. ## More About Validator Nodes A validator node participates in the network's consensus protocol by validating transactions and creating new blocks. Validators play a critical role in ensuring the integrity, security, and decentralization of the network. #### Key Functions of Validators: - **Transaction Validation**: Validators verify the legitimacy of transactions before they are added to the blockchain. - **Block Production**: Validators produce and propose new blocks to the network. This involves reaching consensus with other validators to agree on which transactions should be included in the next block. - **Security and Consensus**: Validators work together to secure the network and ensure that only valid transactions are confirmed. This is done through the Avalanche Consensus protocol, which allows validators to achieve agreement quickly and with high security. ### Primary Network Validators To become a validator on the Primary Network, you must stake **2,000 AVAX**. This will grant you the ability to validate transactions across all three chains in the Primary Network: the P-Chain, C-Chain, and X-Chain. ### Avalanche L1 Validator To become a validator on an Avalanche L1, you must meet the specific validator management criteria for that network. If the L1 operates on a Proof-of-Stake (PoS) model, you will need to stake the required amount of tokens to be eligible. In addition to meeting these criteria, there is a monthly fee of **1.33 AVAX** per validator. # System Requirements (/docs/nodes/system-requirements) --- title: System Requirements description: This document provides information about the system and networking requirements for running an AvalancheGo node. --- ## Hardware and Operating Systems Avalanche is an incredibly lightweight protocol, so nodes can run on commodity hardware. Note that as network usage increases, hardware requirements may change. - **CPU**: Equivalent of 8 AWS vCPU - **RAM**: 8 GiB (16 GiB recommended) - **Storage**: 1 TiB SSD - **OS**: Ubuntu 22.04 or MacOS >= 12 Nodes which choose to use a HDD may get poor and random read/write latencies, therefore reducing performance and reliability. An SSD is strongly suggested. ## Networking To run successfully, AvalancheGo needs to accept connections from the Internet on the network port `9651`. Before you proceed with the installation, you need to determine the networking environment your node will run in. ### On a Cloud Provider If your node is running on a cloud provider computer instance, it will have a static IP. Find out what that static IP is, or set it up if you didn't already. ### On a Home Connection If you're running a node on a computer that is on a residential internet connection, you have a dynamic IP; that is, your IP will change periodically. **For the sake of demonstration, you can ignore the following information.** Otherwise, you will need to set up inbound port forwarding of port `9651` from the internet to the computer the node is installed on. As there are too many models and router configurations, we cannot provide instructions on what exactly to do, but there are online guides to be found (like [this](https://www.noip.com/support/knowledgebase/general-port-forwarding-guide/), or [this](https://www.howtogeek.com/66214/how-to-forward-ports-on-your-router/) ), and your service provider support might help too. Please note that a fully connected Avalanche node maintains and communicates over a couple of thousand of live TCP connections. For some under-powered and older home routers, that might be too much to handle. If that is the case, you may experience lagging on other computers connected to the same router, node getting benched, or failing to sync and similar issues. # Snowman Consensus (/docs/primary-network/avalanche-consensus) --- title: Snowman Consensus description: Learn about the Snowman Consensus protocol. --- Consensus is the task of getting a group of computers (a.k.a. nodes) to come to an agreement on a decision. In blockchain, this means that all the participants in a network have to agree on the changes made to the shared ledger. This agreement is reached through a specific process, a consensus protocol, that ensures that everyone sees the same information and that the information is accurate and trustworthy. ## Snowman Consensus Snowman Consensus is a consensus protocol that is scalable, robust, and decentralized. It combines features of both classical and Nakamoto consensus mechanisms to achieve high throughput, fast finality, and energy efficiency. For the whitepaper, see [here](https://www.avalabs.org/whitepapers). Key Features Include: - Speed: Snowman Consensus provides sub-second, immutable finality, ensuring that transactions are quickly confirmed and irreversible. - Scalability: Snowman Consensus enables high network throughput while ensuring low latency. - Energy Efficiency: Unlike other popular consensus protocols, participation in Snowman Consensus is neither computationally intensive nor expensive. - Adaptive Security: Snowman Consensus is designed to resist various attacks, including sybil attacks, distributed denial-of-service (DDoS) attacks, and collusion attacks. Its probabilistic nature ensures that the consensus outcome converges to the desired state, even when the network is under attack. ## Conceptual Overview Consensus protocols in the Avalanche family operate through repeated sub-sampled voting. When a node is determining whether a [transaction](http://support.avalabs.org/en/articles/4587384-what-is-a-transaction) should be accepted, it asks a small, random subset of [validator nodes](http://support.avalabs.org/en/articles/4064704-what-is-a-blockchain-validator) for their preference. Each queried validator replies with the transaction that it prefers, or thinks should be accepted. Consensus will never include a transaction that is determined to be **invalid**. For example, if you were to submit a transaction to send 100 AVAX to a friend, but your wallet only has 2 AVAX, this transaction is considered **invalid** and will not participate in consensus. If a sufficient majority of the validators sampled reply with the same preferred transaction, this becomes the preferred choice of the validator that inquired. In the future, this node will reply with the transaction preferred by the majority. The node repeats this sampling process until the validators queried reply with the same answer for a sufficient number of consecutive rounds. - The number of validators required to be considered a "sufficient majority" is referred to as "α" (_alpha_). - The number of consecutive rounds required to reach consensus, a.k.a. the "Confidence Threshold," is referred to as "β" (_beta_). - Both α and β are configurable. When a transaction has no conflicts, finalization happens very quickly. When conflicts exist, honest validators quickly cluster around conflicting transactions, entering a positive feedback loop until all correct validators prefer that transaction. This leads to the acceptance of non-conflicting transactions and the rejection of conflicting transactions. ![How Snowman Consensus Works](/images/avalanche-consensus1.png) Snowman Consensus guarantees that if any honest validator accepts a transaction, all honest validators will come to the same conclusion. For a great visualization, check out [this demo](https://tedyin.com/archive/snow-bft-demo/#/snow). ## Deep Dive Into Snowman Consensus ### Intuition First, let's develop some intuition about the protocol. Imagine a room full of people trying to agree on what to get for lunch. Suppose it's a binary choice between pizza and barbecue. Some people might initially prefer pizza while others initially prefer barbecue. Ultimately, though, everyone's goal is to achieve **consensus**. Everyone asks a random subset of the people in the room what their lunch preference is. If more than half say pizza, the person thinks, "OK, looks like things are leaning toward pizza. I prefer pizza now." That is, they adopt the _preference_ of the majority. Similarly, if a majority say barbecue, the person adopts barbecue as their preference. Everyone repeats this process. Each round, more and more people have the same preference. This is because the more people that prefer an option, the more likely someone is to receive a majority reply and adopt that option as their preference. After enough rounds, they reach consensus and decide on one option, which everyone prefers. ### Snowball The intuition above outlines the Snowball Algorithm, which is a building block of Snowman Consensus. Let's review the Snowball algorithm. #### Parameters - _n_: number of participants - _k_ (sample size): between 1 and _n_ - α (quorum size): between 1 and _k_ - β (decision threshold): >= 1 #### Algorithm ``` preference := pizza consecutiveSuccesses := 0 while not decided: ask k random people their preference if >= α give the same response: preference := response with >= α if preference == old preference: consecutiveSuccesses++ else: consecutiveSuccesses = 1 else: consecutiveSuccesses = 0 if consecutiveSuccesses > β: decide(preference) ``` #### Algorithm Explained Everyone has an initial preference for pizza or barbecue. Until someone has _decided_, they query _k_ people (the sample size) and ask them what they prefer. If α or more people give the same response, that response is adopted as the new preference. α is called the _quorum size_. If the new preference is the same as the old preference, the `consecutiveSuccesses` counter is incremented. If the new preference is different then the old preference, the `consecutiveSuccesses` counter is set to `1`. If no response gets a quorum (an α majority of the same response) then the `consecutiveSuccesses` counter is set to `0`. Everyone repeats this until they get a quorum for the same response β times in a row. If one person decides pizza, then every other person following the protocol will eventually also decide on pizza. Random changes in preference, caused by random sampling, cause a network preference for one choice, which begets more network preference for that choice until it becomes irreversible and then the nodes can decide. In our example, there is a binary choice between pizza or barbecue, but Snowball can be adapted to achieve consensus on decisions with many possible choices. The liveness and safety thresholds are parameterizable. As the quorum size, α, increases, the safety threshold increases, and the liveness threshold decreases. This means the network can tolerate more byzantine (deliberately incorrect, malicious) nodes and remain safe, meaning all nodes will eventually agree whether something is accepted or rejected. The liveness threshold is the number of malicious participants that can be tolerated before the protocol is unable to make progress. These values, which are constants, are quite small on the Avalanche Network. The sample size, _k_, is `20`. So when a node asks a group of nodes their opinion, it only queries `20` nodes out of the whole network. The quorum size, α, is `14`. So if `14` or more nodes give the same response, that response is adopted as the querying node's preference. The decision threshold, β, is `20`. A node decides on choice after receiving `20` consecutive quorum (α majority) responses. Snowball is very scalable as the number of nodes on the network, _n_, increases. Regardless of the number of participants in the network, the number of consensus messages sent remains the same because in a given query, a node only queries `20` nodes, even if there are thousands of nodes in the network. Everything discussed to this point is how Avalanche is described in [the Avalanche white-paper](https://assets-global.website-files.com/5d80307810123f5ffbb34d6e/6009805681b416f34dcae012_Avalanche%20Consensus%20Whitepaper.pdf). The implementation of the Snowman Consensus protocol by Ava Labs (namely in AvalancheGo) has some optimizations for latency and throughput. ### Blocks A block is a fundamental component that forms the structure of a blockchain. It serves as a container or data structure that holds a collection of transactions or other relevant information. Each block is cryptographically linked to the previous block, creating a chain of blocks, hence the term "blockchain." In addition to storing a reference of its parent, a block contains a set of transactions. These transactions can represent various types of information, such as financial transactions, smart contract operations, or data storage requests. If a node receives a vote for a block, it also counts as a vote for all of the block's ancestors (its parent, the parents' parent, etc.). ### Finality Snowman Consensus is probabilistically safe up to a safety threshold. That is, the probability that a correct node accepts a transaction that another correct node rejects can be made arbitrarily low by adjusting system parameters. In Nakamoto consensus protocol (as used in Bitcoin and Ethereum, for example), a block may be included in the chain but then be removed and not end up in the canonical chain. This means waiting an hour for transaction settlement. In Avalanche, acceptance/rejection are **final and irreversible** and only take a few seconds. ### Optimizations It's not safe for nodes to just ask, "Do you prefer this block?" when they query validators. In Ava Labs' implementation, during a query a node asks, "Given that this block exists, which block do you prefer?" Instead of getting back a binary yes/no, the node receives the other node's preferred block. Nodes don't only query upon hearing of a new block; they repeatedly query other nodes until there are no blocks processing. Nodes may not need to wait until they get all _k_ query responses before registering the outcome of a poll. If a block has already received _alpha_ votes, then there's no need to wait for the rest of the responses. ### Validators If it were free to become a validator on the Avalanche network, that would be problematic because a malicious actor could start many, many nodes which would get queried very frequently. The malicious actor could make the node act badly and cause a safety or liveness failure. The validators, the nodes which are queried as part of consensus, have influence over the network. They have to pay for that influence with real-world value in order to prevent this kind of ballot stuffing. This idea of using real-world value to buy influence over the network is called Proof of Stake. To become a validator, a node must **bond** (stake) something valuable (**AVAX**). The more AVAX a node bonds, the more often that node is queried by other nodes. When a node samples the network it's not uniformly random. Rather, it's weighted by stake amount. Nodes are incentivized to be validators because they get a reward if, while they validate, they're sufficiently correct and responsive. Avalanche doesn't have slashing. If a node doesn't behave well while validating, such as giving incorrect responses or perhaps not responding at all, its stake is still returned in whole, but with no reward. As long as a sufficient portion of the bonded AVAX is held by correct nodes, then the network is safe, and is live for virtuous transactions. ### Big Ideas Two big ideas in Avalanche are **subsampling** and **transitive voting**. Subsampling has low message overhead. It doesn't matter if there are twenty validators or two thousand validators; the number of consensus messages a node sends during a query remains constant. Transitive voting, where a vote for a block is a vote for all its ancestors, helps with transaction throughput. Each vote is actually many votes in one. ### Loose Ends Transactions are created by users which call an API on an [AvalancheGo](https://github.com/ava-labs/avalanchego) full node or create them using a library such as [AvalancheJS](https://github.com/ava-labs/avalanchejs). ### Other Observations Conflicting transactions are not guaranteed to be live. That's not really a problem because if you want your transaction to be live then you should not issue a conflicting transaction. Snowman is the name of Ava Labs' implementation of the Snowman Consensus protocol for linear chains. If there are no undecided transactions, the Snowman Consensus protocol _quiesces_. That is, it does nothing if there is no work to be done. This makes Avalanche more sustainable than Proof-of-work where nodes need to constantly do work. Avalanche has no leader. Any node can propose a transaction and any node that has staked AVAX can vote on every transaction, which makes the network more robust and decentralized. ## Why Do We Care? Avalanche is a general consensus engine. It doesn't matter what type of application is put on top of it. The protocol allows the decoupling of the application layer from the consensus layer. If you're building a dapp on Avalanche then you just need to define a few things, like how conflicts are defined and what is in a transaction. You don't need to worry about how nodes come to an agreement. The consensus protocol is a black box that put something into it and it comes back as accepted or rejected. Avalanche can be used for all kinds of applications, not just P2P payment networks. Avalanche's Primary Network has an instance of the Ethereum Virtual Machine, which is backward compatible with existing Ethereum Dapps and dev tooling. The Ethereum consensus protocol has been replaced with Snowman Consensus to enable lower block latency and higher throughput. Avalanche is very performant. It can process thousands of transactions per second with one to two second acceptance latency. ## Summary Snowman Consensus is a radical breakthrough in distributed systems. It represents as large a leap forward as the classical and Nakamoto consensus protocols that came before it. Now that you have a better understanding of how it works, check out other documentations for building game-changing Dapps and financial instruments on Avalanche. # AVAX Token (/docs/primary-network/avax-token) --- title: AVAX Token description: Learn about the native token of Avalanche Primary Network. --- AVAX is the native utility token of Avalanche. It's a hard-capped, scarce asset that is used to pay for fees, secure the platform through staking, and provide a basic unit of account between the multiple Avalanche L1s created on Avalanche. `1 nAVAX` is equal to `0.000000001 AVAX`. Use the [AVAX Unit Converter](/console/primary-network/unit-converter) to convert between different AVAX denominations. ## Utility AVAX is a capped-supply (up to 720M) resource in the Avalanche ecosystem that's used to power the network. AVAX is used to secure the ecosystem through staking and for day-to-day operations like issuing transactions. AVAX represents the weight that each node has in network decisions. No single actor owns the Avalanche Network, so each validator in the network is given a proportional weight in the network's decisions corresponding to the proportion of total stake that they own through proof of stake (PoS). Any entity trying to execute a transaction on Avalanche Primary Network pays a corresponding fee (commonly known as "gas") to run it on the network. The fees used to execute a transaction on Avalanche is burned, or permanently removed from circulating supply. ## Tokenomics A fixed amount of 360M AVAX was minted at genesis, but a small amount of AVAX is constantly minted as a reward to validators. The protocol rewards validators for good behavior by minting them AVAX rewards at the end of their staking period. The minting process offsets the AVAX burned by transactions fees. While AVAX is still far away from its supply cap, it will almost always remain an inflationary asset. Avalanche does not take away any portion of a validator's already staked tokens (commonly known as "slashing") for negligent/malicious staking periods, however this behavior is disincentivized as validators who attempt to do harm to the network would expend their node's computing resources for no reward. AVAX is minted according to the following formula, where $R_j$ is the total number of tokens at year $j$, with $R_1 = 360M$, and $R_l$ representing the last year that the values of $\gamma,\lambda \in \R$ were changed; $c_j$ is the yet un-minted supply of coins to reach $720M$ at year $j$ such that $c_j \leq 360M$; $u$ represents a staker, with $u.s_{amount}$ representing the total amount of stake that $u$ possesses, and $u.s_{time}$ the length of staking for $u$. AVAX is minted according to the following formula, where $R_j$ is the total number of tokens at: $$ R_j = R_l + \sum_{\forall u} \rho(u.s_{amount}, u.s_{time}) \times \frac{c_j}{L} \times \left( \sum_{i=0}^{j}\frac{1}{\left(\gamma + \frac{1}{1 + i^\lambda}\right)^i} \right) $$ where, $$ L = \left(\sum_{i=0}^{\infty} \frac{1}{\left(\gamma + \frac{1}{1 + i^\lambda} \right)^i} \right) $$ At genesis, $c_1 = 360M$. The values of $\gamma$ and $\lambda$ are governable, and if changed, the function is recomputed with the new value of $c_*$. We have that $\sum_{*}\rho(*) \le 1$. $\rho(*)$ is a linear function that can be computed as follows ($u.s_{time}$ is measured in weeks, and $u.s_{amount}$ is measured in AVAX tokens): $$ \rho(u.s_{amount}, u.s_{time}) = (0.002 \times u.s_{time} + 0.896) \times \frac{u.s_{amount}}{R_j} $$ If the entire supply of tokens at year $j$ is staked for the maximum amount of staking time (one year, or 52 weeks), then $\sum_{\forall u}\rho(u.s_{amount}, u.s_{time}) = 1$. If, instead, every token is staked continuously for the minimal stake duration of two weeks, then $\sum_{\forall u}\rho(u.s_{amount}, u.s_{time}) = 0.9$. Therefore, staking for the maximum amount of time incurs an additional 11.11% of tokens minted, incentivizing stakers to stake for longer periods. Due to the capped-supply, the above function guarantees that AVAX will never exceed a total of $720M$ tokens, or $\lim_{j \to \infty} R(j) = 720M$. # Exchange Integration (/docs/primary-network/exchange-integration) --- title: Exchange Integration description: Learn how to integrate your exchange with the EVM-Compatible Avalanche C-Chain. --- ## Overview The objective of this document is to provide a brief overview of how to integrate with the EVM-Compatible Avalanche C-Chain. For teams that already support ETH, supporting the C-Chain is as straightforward as spinning up an Avalanche node (which has the [same API](https://ethereum.org/en/developers/docs/apis/json-rpc/) as [`go-ethereum`](https://geth.ethereum.org/docs/rpc/server)) and populating Avalanche's ChainID (43114) when constructing transactions. Additionally, Ava Labs maintains an implementation of the [Rosetta API](https://docs.cdp.coinbase.com/mesh/docs/welcome) for the C-Chain called [avalanche-rosetta](https://github.com/ava-labs/avalanche-rosetta). You can learn more about this standardized integration path on the attached Rosetta API website. ## Integration Using EVM Endpoints ### Running an Avalanche Node If you want to build your node form source or include it in a docker image, reference the [AvalancheGo GitHub repository](https://github.com/ava-labs/avalanchego). To quickly get up and running, you can use the [node installation script](/docs/nodes/run-a-node/using-install-script/installing-avalanche-go) that automates installing and updating AvalancheGo node as a `systemd` service on Linux, using prebuilt binaries. ### Configuring an Avalanche Node All configuration options and their default values are described [here](/docs/nodes/configure/configs-flags). You can supply configuration options on the command line, or use a config file, which can be easier to work with when supplying many options. You can specify the config file location with `—config-file=config.json`, where `config.json` is a JSON file whose keys and values are option names and values. Individual chains, including the C-Chain, have their own configuration options which are separate from the node-level options. These can also be specified in a config file. For more details, see [here](/docs/nodes/chain-configs/primary-network/c-chain). The C-Chain config file should be at `$HOME/.avalanchego/configs/chains/C/config.json`. You can also tell AvalancheGo to look somewhere else for the C-Chain config file with option `--chain-config-dir`. An example C-Chain config file: If you need Ethereum's [Archive Node](https://ethereum.org/en/developers/docs/nodes-and-clients/#archive-node) functionality, you need to disable C-Chain pruning, which has been enabled by default since AvalancheGo v1.4.10. To disable pruning, include `"pruning-enabled": false` in the C-Chain config file as shown below. ```json { "snowman-api-enabled": false, "coreth-admin-api-enabled": false, "local-txs-enabled": true, "pruning-enabled": false, "eth-apis": [ "internal-eth", "internal-blockchain", "internal-transaction", "internal-tx-pool", "internal-account", "internal-personal", "debug-tracer", "web3", "eth", "eth-filter", "admin", "net" ] } ``` ### Interacting with the C-Chain Interacting with the C-Chain is identical to interacting with [`go-ethereum`](https://geth.ethereum.org/). You can find the reference material for C-Chain API [here](/docs/rpcs/c-chain). Please note that `personal_` namespace is turned off by default. To turn it on, you need to pass the appropriate command line switch to your node, like in the above config example. ## Integration Using Rosetta [Rosetta](https://docs.cdp.coinbase.com/mesh/docs/welcome) is an open-source specification and set of tools that makes integrating with different blockchain networks easier by presenting the same set of APIs for every network. The Rosetta API is made up of 2 core components, the [Data API](https://docs.cdp.coinbase.com/mesh/docs/api-data) and the [Construction API](https://docs.cdp.coinbase.com/mesh/docs/api-construction). Together, these APIs allow for anyone to read and write to blockchains in a standard format over a standard communication protocol. The specifications for these APIs can be found in the [rosetta-specifications](https://github.com/coinbase/rosetta-specifications) repository. You can find the Rosetta server implementation for Avalanche C-Chain [here](https://github.com/ava-labs/avalanche-rosetta), all you need to do is install and run the server with proper configuration. It comes with a `Dockerfile` that packages both the server and the Avalanche client. Detailed instructions can be found in the linked repository. ## Constructing Transactions Avalanche C-Chain transactions are identical to standard EVM transactions with 2 exceptions: - They must be signed with Avalanche's ChainID (43114). - The detailed dynamic gas fee can be found [here](/docs/rpcs/other/guides/txn-fees#c-chain-fees). For development purposes, Avalanche supports all the popular tooling for Ethereum, so developers familiar with Ethereum and Solidity can feel right at home. Popular development environments include: - [Remix IDE](https://remix.ethereum.org/) - [thirdweb](https://thirdweb.com/) - [Hardhat](https://hardhat.org/) ## Ingesting On-Chain Data You can use any standard way of ingesting on-chain data you use for Ethereum network. ### Determining Finality Avalanche consensus provides fast and irreversible finality with 1-2 seconds. To query the most up-to-date finalized block, query any value (that is block, balance, state, etc) with the `latest` parameter. If you query above the last finalized block (that is eth_blockNumber returns 10 and you query 11), an error will be thrown indicating that unfinalized data cannot be queried (as of `avalanchego@v1.3.2`). ### (Optional) Custom Golang SDK If you plan on extracting data from the C-Chain into your own systems using Golang, we recommend using our custom [`ethclient`](https://github.com/ava-labs/avalanchego/tree/master/graft/coreth/ethclient). The standard `go-ethereum` Ethereum client does not compute block hashes correctly (when you call `block.Hash()`) because it doesn't take into account the added [ExtDataHash](https://github.com/ava-labs/avalanchego/blob/master/graft/coreth/core/types/block.go#L98) header field in Avalanche C-Chain blocks, which is used move AVAX between chains (X-Chain and P-Chain). You can read more about our multi-chain abstraction [here](/docs/primary-network) (out of scope for a normal C-Chain integration). If you plan on reading JSON responses directly or use web3.js (doesn't recompute hash received over the wire) to extract on-chain transaction data/logs/receipts, you shouldn't have any issues! ## Support If you have any problems or questions, reach out either directly to our developers, or on our public [Discord](https://chat.avalabs.org/) server. # Primary Network (/docs/primary-network) --- title: Primary Network description: Learn about the Avalanche Primary Network and its three blockchains. --- import { Network, Layers, Terminal, ArrowRight, Database, Package } from 'lucide-react'; Avalanche is a heterogeneous network of blockchains. As opposed to homogeneous networks, where all applications reside in the same chain, heterogeneous networks allow separate chains to be created for different applications. ![Primary Network Architecture](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/multi-chain-architecture/multi-chain.png) The Primary Network is a special [Avalanche L1](/docs/avalanche-l1s) that runs three blockchains: - The Contract Chain [(C-Chain)](/docs/primary-network#c-chain-contract-chain) - The Platform Chain [(P-Chain)](/docs/primary-network#p-chain-platform-chain) - The Exchange Chain [(X-Chain)](/docs/primary-network#x-chain-exchange-chain) Primary Network"] PN ==> XC["Exchange (X) Chain
━━━━━━━━━━━━━━━
Snowman Consensus Protocol"] PN ==> PC["Platform (P) Chain
━━━━━━━━━━━━━━━
Snowman Consensus Protocol"] PN ==> CC["Contract (C) Chain
━━━━━━━━━━━━━━━
Snowman Consensus Protocol"] XC --> XF1["Creates Assets"] XC --> XF2["Exchanges Assets"] PC --> PF1["Validator Registry"] PC --> PF2["Creates Subnets / Layer 1s"] CC --> CF1["Executes EVM Contracts"] CC --> CF2["Smart Contracts"] XF1 -.-> A1["Asset 1"] XF1 -.-> A2["Asset 2"] XF2 -.-> AV["AVAX"] PF2 -.-> C1["Custom Chain 1"] PF2 -.-> C2["Custom Chain 2"] CF2 -.-> N1["NFT"] CF2 -.-> E1["ERC20"] CF2 -.-> D1["DAPP"] style PN fill:#e74c3c,stroke:#c0392b,stroke-width:4px,color:#fff,rx:15,ry:15 style XC fill:#3498db,stroke:#2980b9,stroke-width:3px,color:#fff,rx:10,ry:10 style PC fill:#9b59b6,stroke:#8e44ad,stroke-width:3px,color:#fff,rx:10,ry:10 style CC fill:#16a085,stroke:#138d75,stroke-width:3px,color:#fff,rx:10,ry:10 style XF1 fill:#5dade2,stroke:#3498db,stroke-width:2px,color:#fff,rx:8,ry:8 style XF2 fill:#5dade2,stroke:#3498db,stroke-width:2px,color:#fff,rx:8,ry:8 style PF1 fill:#bb8fce,stroke:#9b59b6,stroke-width:2px,color:#fff,rx:8,ry:8 style PF2 fill:#bb8fce,stroke:#9b59b6,stroke-width:2px,color:#fff,rx:8,ry:8 style CF1 fill:#52be80,stroke:#16a085,stroke-width:2px,color:#fff,rx:8,ry:8 style CF2 fill:#52be80,stroke:#16a085,stroke-width:2px,color:#fff,rx:8,ry:8 style A1 fill:#85c1e9,stroke:#5dade2,stroke-width:1px,color:#2c3e50,rx:5,ry:5 style A2 fill:#85c1e9,stroke:#5dade2,stroke-width:1px,color:#2c3e50,rx:5,ry:5 style AV fill:#85c1e9,stroke:#5dade2,stroke-width:1px,color:#2c3e50,rx:5,ry:5 style C1 fill:#d7bde2,stroke:#bb8fce,stroke-width:1px,color:#2c3e50,rx:5,ry:5 style C2 fill:#d7bde2,stroke:#bb8fce,stroke-width:1px,color:#2c3e50,rx:5,ry:5 style N1 fill:#a3e4d7,stroke:#52be80,stroke-width:1px,color:#2c3e50,rx:5,ry:5 style E1 fill:#a3e4d7,stroke:#52be80,stroke-width:1px,color:#2c3e50,rx:5,ry:5 style D1 fill:#a3e4d7,stroke:#52be80,stroke-width:1px,color:#2c3e50,rx:5,ry:5 `} /> Avalanche Mainnet is comprised of the Primary Network and all deployed Avalanche L1s. A node can become a validator for the Primary Network by staking at least **2,000 AVAX**. ### C-Chain (Contract Chain) The **C-Chain** is an implementation of the Ethereum Virtual Machine (EVM). The [C-Chain's API](/docs/rpcs/c-chain) supports Geth's API and supports the deployment and execution of smart contracts written in Solidity. The C-Chain is an instance of the [Coreth](https://github.com/ava-labs/avalanchego/tree/master/graft/coreth) Virtual Machine. | Property | Mainnet | Fuji Testnet | |----------|---------|--------------| | **Network Name** | Avalanche C-Chain | Avalanche Fuji C-Chain | | **Chain ID** | 43114 (0xA86A) | 43113 (0xA869) | | **Currency** | AVAX | AVAX | | **RPC URL** | https://api.avax.network/ext/bc/C/rpc | https://api.avax-test.network/ext/bc/C/rpc | | **Explorer** | https://subnets.avax.network/c-chain | https://subnets-test.avax.network/c-chain | | **Faucet** | - | [Get Test AVAX](/console/primary-network/faucet) | | **Add to Wallet** | | | ### P-Chain (Platform Chain) The **P-Chain** is responsible for all validator and Avalanche L1-level operations. The [P-Chain API](/docs/rpcs/p-chain) supports the creation of new blockchains and Avalanche L1s, the addition of validators to Avalanche L1s, staking operations, and other platform-level operations. The P-Chain is an instance of the [Platform Virtual Machine](https://github.com/ava-labs/avalanchego/tree/master/vms/platformvm). | Property | Mainnet | Fuji Testnet | |----------|---------|--------------| | **RPC URL** | https://api.avax.network/ext/bc/P | https://api.avax-test.network/ext/bc/P | | **Currency** | AVAX | AVAX | | **Explorer** | https://subnets.avax.network/p-chain | https://subnets-test.avax.network/p-chain | ### X-Chain (Exchange Chain) The **X-Chain** is responsible for operations on digital smart assets known as **Avalanche Native Tokens**. A smart asset is a representation of a real-world resource (for example, equity, or a bond) with sets of rules that govern its behavior, like "can't be traded until tomorrow." The [X-Chain API](/docs/rpcs/x-chain) supports the creation and trade of Avalanche Native Tokens. One asset traded on the X-Chain is AVAX. When you issue a transaction to a blockchain on Avalanche, you pay a fee denominated in AVAX. The X-Chain is an instance of the Avalanche Virtual Machine (AVM). | Property | Mainnet | Fuji Testnet | |----------|---------|--------------| | **RPC URL** | https://api.avax.network/ext/bc/X | https://api.avax-test.network/ext/bc/X | | **Currency** | AVAX | AVAX | | **Explorer** | https://subnets.avax.network/x-chain | https://subnets-test.avax.network/x-chain | ## Explore More

Avalanche L1s

Discover how to build sovereign networks with custom rules and token economics.

Data APIs

Access data APIs for the C-Chain, P-Chain, and X-Chain.

Console

Access developer tools, deploy contracts, and manage your blockchain infrastructure.

# Virtual Machines (/docs/primary-network/virtual-machines) --- title: Virtual Machines description: Learn about blockchain VMs and how you can build a custom VM-enabled blockchain in Avalanche. --- A **Virtual Machine** (VM) is the blueprint for a blockchain, meaning it defines a blockchain's complete application logic by specifying the blockchain's state, state transitions, transaction rules, and API interface. Developers can use the same VM to create multiple blockchains, each of which follows identical rules but is independent of all others. All Avalanche validators of the **Avalanche Primary Network** are required to run three VMs: - **Coreth**: Defines the Contract Chain (C-Chain); supports smart contract functionality and is EVM-compatible. - **Platform VM**: Defines the Platform Chain (P-Chain); supports operations on staking and Avalanche L1s. - **Avalanche VM**: Defines the Exchange Chain (X-Chain); supports operations on Avalanche Native Tokens. All three can easily be run on any computer with [AvalancheGo](/docs/nodes). ## Custom VMs on Avalanche Developers with advanced use-cases for utilizing distributed ledger technology are often forced to build everything from scratch - networking, consensus, and core infrastructure - before even starting on the actual application. Avalanche eliminates this complexity by: - Providing VMs as simple blueprints for defining blockchain behavior - Supporting development in any programming language with familiar tools - Handling all low-level infrastructure automatically This lets developers focus purely on building their dApps, ecosystems, and communities, rather than wrestling with blockchain fundamentals. ### How Custom VMs Work Customized VMs can communicate with Avalanche over a language agnostic request-response protocol known as [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call). This allows the VM framework to open a world of endless possibilities, as developers can implement their dApps using the languages, frameworks, and libraries of their choice. Validators can install additional VMs on their node to validate additional [Avalanche L1s](/docs/avalanche-l1s) in the Avalanche ecosystem. In exchange, validators receive staking rewards in the form of a reward token determined by the Avalanche L1s. ## Building a Custom VM You can start building your first custom virtual machine in two ways: 1. Use the ready-to-deploy Subnet-EVM for Solidity-based development 2. Create a custom VM in Golang, Rust, or your preferred language The choice depends on your needs. Subnet-EVM provides a quick start with Ethereum compatibility, while custom VMs offer maximum flexibility. ### Golang Examples See here for a tutorial on [How to Build a Simple Golang VM](/docs/avalanche-l1s/golang-vms/simple-golang-vm). ### Rust Examples See here for a tutorial on [How to Build a Simple Rust VM](/docs/avalanche-l1s/rust-vms/setting-up-environment). # RPC APIs (/docs/rpcs) --- title: RPC APIs description: AvalancheGo RPC API References for interacting with Avalanche nodes --- # RPC APIs This section contains comprehensive documentation for all RPC (Remote Procedure Call) APIs available in the Avalanche ecosystem. ## Chain-Specific APIs ### C-Chain (Contract Chain) The C-Chain is an instance of the Ethereum Virtual Machine (EVM). Documentation for C-Chain RPC methods and transaction formats. ### P-Chain (Platform Chain) The P-Chain manages validators, staking, and subnets. Documentation for P-Chain RPC methods and transaction formats. ### X-Chain (Exchange Chain) The X-Chain is responsible for asset creation and trading. Documentation for X-Chain RPC methods and transaction formats. ### Subnet-EVM The Subnet-EVM is an instance of the EVM for Subnet / Layer 1 chains. Documentation for Subnet-EVM RPC methods and transaction formats. ## Other APIs Additional RPC APIs for node administration, health monitoring, indexing, metrics, and more. # Data API vs RPC (/docs/api-reference/data-api/data-vs-rpc) --- title: Data API vs RPC description: Comparison of the Data API and RPC methods icon: Server --- In the rapidly evolving world of Web3 development, efficiently retrieving token balances for a user's address is a fundamental requirement. Whether you're building DeFi platforms, wallets, analytics tools, or exchanges, displaying accurate token balances is crucial for user engagement and trust. A typical use case involves showing a user's token portfolio in a wallet application, in this case, we have sAvax and USDC. title Developers generally have two options to fetch this data: 1. **Using RPC methods to index blockchain data on their own** 2. **Leveraging an indexer provider like the Data API** While both methods aim to achieve the same goal, the Data API offers a more efficient, scalable, and developer-friendly solution. This article delves into why using the Data API is better than relying on traditional RPC (Remote Procedure Call) methods. ### What Are RPC methods and their challenges? Remote Procedure Call (RPC) methods allow developers to interact directly with blockchain nodes. One of their key advantages is that they are standardized and universally understood by blockchain developers across different platforms. With RPC, you can perform tasks such as querying data, submitting transactions, and interacting with smart contracts. These methods are typically low-level and synchronous, meaning they require a deep understanding of the blockchain’s architecture and specific command structures. You can refer to the [official documentation](https://ethereum.org/en/developers/docs/apis/json-rpc/) to gain a more comprehensive understanding of the JSON-RPC API. Here’s an example using the `eth_getBalance` method to retrieve the native balance of a wallet: ```bash curl --location 'https://api.avax.network/ext/bc/C/rpc' \ --header 'Content-Type: application/json' \ --data '{"method":"eth_getBalance","params":["0x8ae323046633A07FB162043f28Cea39FFc23B50A", "latest"],"id":1,"jsonrpc":"2.0"}' ``` This call returns the following response: ```json { "jsonrpc": "2.0", "id": 1, "result": "0x284476254bc5d594" } ``` The balance in this wallet is 2.9016 AVAX. However, despite the wallet holding multiple tokens such as USDC, the `eth_getBalance` method only returns the AVAX amount and it does so in Wei and in hexadecimal format. This is not particularly human-readable, adding to the challenge for developers who need to manually convert the balance to a more understandable format. #### No direct RPC methods to retrieve token balances Despite their utility, RPC methods come with significant limitations when it comes to retrieving detailed token and transaction data. Currently, RPC methods do not provide direct solutions for the following: * **Listing all tokens held by a wallet**: There is no RPC method that provides a complete list of ERC-20 tokens owned by a wallet. * **Retrieving all transactions for a wallet**: : There is no direct method for fetching all transactions associated with a wallet. * **Getting ERC-20/721/1155 token balances**: The `eth_getBalance` method only returns the balance of the wallet’s native token (such as AVAX on Avalanche) and cannot be used to retrieve ERC-20/721/1155 token balances. To achieve these tasks using RPC methods alone, you would need to: * **Query every block for transaction logs**: Scan the entire blockchain, which is resource-intensive and impractical. * **Parse transaction logs**: Identify and extract ERC-20 token transfer events from each transaction. * **Aggregate data**: Collect and process this data to compute balances and transaction histories. #### Manual blockchain indexing is difficult and costly Using RPC methods to fetch token balances involves an arduous process: 1. You must connect to a node and subscribe to new block events. 2. For each block, parse every transaction to identify ERC-20 token transfers involving the user's address. 3. Extract contract addresses and other relevant data from the parsed transactions. 4. Compute balances by processing transfer events. 5. Store the processed data in a database for quick retrieval and aggregation. #### Why this is difficult: * **Resource-Intensive**: Requires significant computational power and storage to process and store blockchain data. * **Time-consuming**: Processing millions of blocks and transactions can take an enormous amount of time. * **Complexity**: Handling edge cases like contract upgrades, proxy contracts, and non-standard implementations adds layers of complexity. * **Maintenance**: Keeping the indexed data up-to-date necessitates continuous synchronization with new blocks being added to the blockchain. * **High Costs**: Associated with servers, databases, and network bandwidth. ### The Data API Advantage The Data API provides a streamlined, efficient, and scalable solution for fetching token balances. Here's why it's the best choice: With a single API call, you can retrieve all ERC-20 token balances for a user's address: ```javascript avalancheSDK.data.evm.balances.listErc20Balances({ address: "0xYourAddress", }); ``` Sample Response: ```json { "erc20TokenBalances": [ { "ercType": "ERC-20", "chainId": "43114", "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "price": { "value": 1.0, "currencyCode": "usd" }, "balance": "15000000", "balanceValue": { "currencyCode": "usd", "value": 9.6 }, "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/e50058c1-2296-4e7e-91ea-83eb03db95ee/8db2a492ce64564c96de87c05a3756fd/43114-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E.png" } // Additional tokens... ] } ``` As you can see with a single call the API returns an array of token balances for all the wallet tokens, including: * **Token metadata**: Contract address, name, symbol, decimals. * **Balance information**: Token balance in both hexadecimal and decimal formats, Also retrieves balances of native assets like ETH or AVAX. * **Price data**: Current value in USD or other supported currencies, saving you the effort of integrating another API. * **Visual assets**: Token logo URI for better user interface integration. If you’re building a wallet, DeFi app, or any application that requires displaying balances, transaction history, or smart contract interactions, relying solely on RPC methods can be challenging. Just as there’s no direct RPC method to retrieve token balances, there’s also no simple way to fetch all transactions associated with a wallet, especially for ERC-20, ERC-721, or ERC-1155 token transfers. However, by using the Data API, you can retrieve all token transfers for a given wallet **with a single API call**, making the process much more efficient. This approach simplifies tracking and displaying wallet activity without the need to manually scan the entire blockchain. Below are two examples that demonstrate the power of the Data API: in the first, it returns all ERC transfers, including ERC-20, ERC-721, and ERC-1155 tokens, and in the second, it shows all internal transactions, such as when one contract interacts with another. [Lists ERC transfers](/data-api/evm-transactions/list-erc-transfers) for an ERC-20, ERC-721, or ERC-1155 contract address. ```javascript theme={null} import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { const result = await avalancheSDK.data.evm.transactions.listTransfers({ startBlock: 6479329, endBlock: 6479330, pageSize: 10, address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", }); for await (const page of result) { // Handle the page console.log(page); } } run(); ``` Example response ```json theme={null} { "nextPageToken": "", "transfers": [ { "blockNumber": "339", "blockTimestamp": 1648672486, "blockHash": "0x17533aeb5193378b9ff441d61728e7a2ebaf10f61fd5310759451627dfca2e7c", "txHash": "0x3e9303f81be00b4af28515dab7b914bf3dbff209ea10e7071fa24d4af0a112d4", "from": { "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg", "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" }, "to": { "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg", "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" }, "logIndex": 123, "value": "10000000000000000000", "erc20Token": { "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg", "ercType": "ERC-20", "price": { "currencyCode": "usd", "value": "42.42" } } } ] } ``` [Returns a list of internal transactions](/data-api/evm-transactions/list-internal-transactions) for an address and chain. Filterable by block range. ```javascript theme={null} import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { const result = await avalancheSDK.data.evm.transactions.listInternalTransactions({ startBlock: 6479329, endBlock: 6479330, pageSize: 10, address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", }); for await (const page of result) { // Handle the page console.log(page); } } run(); ``` Example response ```json theme={null} { "nextPageToken": "", "transactions": [ { "blockNumber": "339", "blockTimestamp": 1648672486, "blockHash": "0x17533aeb5193378b9ff441d61728e7a2ebaf10f61fd5310759451627dfca2e7c", "txHash": "0x3e9303f81be00b4af28515dab7b914bf3dbff209ea10e7071fa24d4af0a112d4", "from": { "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg", "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" }, "to": { "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg", "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" }, "internalTxType": "UNKNOWN", "value": "10000000000000000000", "isReverted": true, "gasUsed": "", "gasLimit": "" } ] } ``` ### Conclusion Using the Data API over traditional RPC methods for fetching token balances offers significant advantages: * **Efficiency**: Retrieve all necessary information in a single API call. * **Simplicity**: Eliminates complex data processing and reduces development time. * **Scalability**: Handles large volumes of data efficiently, suitable for real-time applications. * **Comprehensive Data**: Provides enriched information, including token prices and logos. * **Reliability**: Ensures data accuracy and consistency without the need for extensive error handling. For developers building Web3 applications, leveraging the Data API is the smarter choice. It not only simplifies your codebase but also enhances the user experience by providing accurate and timely data. If you’re building cutting-edge Web3 applications, this API is the key to improving your workflow and performance. Whether you’re developing DeFi solutions, wallets, or analytics platforms, take your project to the next level. [Start today with the Data API](/data-api/getting-started) and experience the difference! # Getting Started (/docs/api-reference/data-api/getting-started) --- title: Getting Started description: Getting Started with the Data API icon: Book --- To begin, create your free account by visiting [Builder Hub Console](https://build.avax.network/login?callbackUrl=%2Fconsole%2Futilities%2Fdata-api-keys). Once the account is created: 1. Navigating to [**Data API Keys**](https://build.avax.network/console/utilities/data-api-keys) 2. Click on **Create API Key** 3. Set an alias and click on **create** 4. Copy the the value Always keep your API keys in a secure environment. Never expose them in public repositories, such as GitHub, or share them with unauthorized individuals. Compromised API keys can lead to unauthorized access and potential misuse of your account. With your API Key you can start making queries, for example to get the latest block on the C-chain(43114): ```bash theme={null} curl --location 'https://data-api.avax.network/v1/chains/43114/blocks' \ --header 'accept: application/json' \ --header 'x-glacier-api-key: ' \ ``` And you should see something like this: ```json theme={null} { "blocks": [ { "blockNumber": "49889407", "blockTimestamp": 1724990250, "blockHash": "0xd34becc82943e3e49048cdd3f75b80a87e44eb3aed6b87cc06867a7c3b9ee213", "txCount": 1, "baseFee": "25000000000", "gasUsed": "53608", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0xf4917efb4628a1d8f4d101b3d15bce9826e62ef2c93c3e16ee898d27cf02f3d4", "feesSpent": "1435117553916960", "cumulativeTransactions": "500325352" }, { "blockNumber": "49889406", "blockTimestamp": 1724990248, "blockHash": "0xf4917efb4628a1d8f4d101b3d15bce9826e62ef2c93c3e16ee898d27cf02f3d4", "txCount": 2, "baseFee": "25000000000", "gasUsed": "169050", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0x2a54f142fa3acee92a839b071bb6c7cca7abc2a797cf4aac68b07f79406ac0cb", "feesSpent": "4226250000000000", "cumulativeTransactions": "500325351" }, { "blockNumber": "49889405", "blockTimestamp": 1724990246, "blockHash": "0x2a54f142fa3acee92a839b071bb6c7cca7abc2a797cf4aac68b07f79406ac0cb", "txCount": 4, "baseFee": "25000000000", "gasUsed": "618638", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0x0cda1bb5c86e790976c9330c9fc26e241a705afbad11a4caa44df1c81058451d", "feesSpent": "16763932426044724", "cumulativeTransactions": "500325349" }, { "blockNumber": "49889404", "blockTimestamp": 1724990244, "blockHash": "0x0cda1bb5c86e790976c9330c9fc26e241a705afbad11a4caa44df1c81058451d", "txCount": 3, "baseFee": "25000000000", "gasUsed": "254544", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0x60e55dd9eacc095c07f50a73e02d81341c406584f7abbf5d10d938776a4c893c", "feesSpent": "6984642298020000", "cumulativeTransactions": "500325345" }, { "blockNumber": "49889403", "blockTimestamp": 1724990242, "blockHash": "0x60e55dd9eacc095c07f50a73e02d81341c406584f7abbf5d10d938776a4c893c", "txCount": 2, "baseFee": "25000000000", "gasUsed": "65050", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0xa3e9f91f45a85ed00b8ebe8e5e976ed1a1f52612143eddd3de9d2588d05398b8", "feesSpent": "1846500000000000", "cumulativeTransactions": "500325342" }, { "blockNumber": "49889402", "blockTimestamp": 1724990240, "blockHash": "0xa3e9f91f45a85ed00b8ebe8e5e976ed1a1f52612143eddd3de9d2588d05398b8", "txCount": 2, "baseFee": "25000000000", "gasUsed": "74608", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0x670db772edfc2fdae322d55473ba0670690aed6358a067a718492c819d63356a", "feesSpent": "1997299851936960", "cumulativeTransactions": "500325340" }, { "blockNumber": "49889401", "blockTimestamp": 1724990238, "blockHash": "0x670db772edfc2fdae322d55473ba0670690aed6358a067a718492c819d63356a", "txCount": 1, "baseFee": "25000000000", "gasUsed": "273992", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0x75742cf45383ce54823690b9dd2e85a743be819281468163d276f145d077902a", "feesSpent": "7334926295195040", "cumulativeTransactions": "500325338" }, { "blockNumber": "49889400", "blockTimestamp": 1724990236, "blockHash": "0x75742cf45383ce54823690b9dd2e85a743be819281468163d276f145d077902a", "txCount": 1, "baseFee": "25000000000", "gasUsed": "291509", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0xe5055eae3e1fd2df24b61e9c691f756c97e5619cfc66b69cbcb6025117d1bde7", "feesSpent": "7724988500000000", "cumulativeTransactions": "500325337" }, { "blockNumber": "49889399", "blockTimestamp": 1724990234, "blockHash": "0xe5055eae3e1fd2df24b61e9c691f756c97e5619cfc66b69cbcb6025117d1bde7", "txCount": 8, "baseFee": "25000000000", "gasUsed": "824335", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0xbcacff928f7dd20cc1522155e7c9b9716997914b53ab94034b813c3f207174ef", "feesSpent": "21983004380692400", "cumulativeTransactions": "500325336" }, { "blockNumber": "49889398", "blockTimestamp": 1724990229, "blockHash": "0xbcacff928f7dd20cc1522155e7c9b9716997914b53ab94034b813c3f207174ef", "txCount": 1, "baseFee": "25000000000", "gasUsed": "21000", "gasLimit": "15000000", "gasCost": "0", "parentHash": "0x0b686812078429d33e4224d2b48bd26b920db8dbb464e7f135d980759ca7e947", "feesSpent": "562182298020000", "cumulativeTransactions": "500325328" } ], "nextPageToken": "9f9e1d25-14a9-49f4-8742-fd4bf12f7cd8" } ``` Congratulations! You’ve successfully set up your account and made your first query to the Data API 🚀🚀🚀 # Data API (/docs/api-reference/data-api) --- title: Data API description: Access comprehensive blockchain data for Avalanche networks icon: Database --- ### What is the Data API? The Data API provides web3 application developers with multi-chain data related to Avalanche's primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata. Data API The [Data API](/docs/api-reference/data-api), along with the [Metrics API](/docs/api-reference/metrics-api), are the engines behind the [Avalanche Explorer](https://subnets.avax.network/stats/) and the [Core wallet](https://core.app/en/). They are used to display transactions, logs, balances, NFTs, and more. The data and visualizations presented are all powered by these APIs, offering real-time and historical insights that are essential for building sophisticated, data-driven blockchain products. ### Features * **Extensive L1 Support**: Gain access to data from over 100+ L1s across both mainnet and testnet. If an L1 is listed on the [Avalanche Explorer](https://subnets.avax.network/), you can query its data using the Data API. * **Transactions and UTXOs**: easily retrieve details related to transactions, UTXOs, and token transfers from Avalanche EVMs, Ethereum, and Avalanche's Primary Network - the P-Chain, X-Chain and C-Chain. * **Blocks**: retrieve latest blocks and block details * **Balances**: fetch balances of native, ERC-20, ERC-721, and ERC-1155 tokens along with relevant metadata. * **Tokens**: augment your user experience with asset details. * **Staking**: get staking related data for active and historical validations. ### Supported Chains Avalanche’s architecture supports a diverse ecosystem of interconnected L1 blockchains, each operating independently while retaining the ability to seamlessly communicate with other L1s within the network. Central to this architecture is the Primary Network—Avalanche’s foundational network layer, which all validators are required to validate prior to [ACP-77](/docs/acps/77-reinventing-subnets). The Primary Network runs three essential blockchains: * The Contract Chain (C-Chain) * The Platform Chain (P-Chain) * The Exchange Chain (X-Chain) However, with the implementation of [ACP-77](/docs/acps/77-reinventing-subnets), this requirement will change. Subnet Validators will be able to operate independently of the Primary Network, allowing for more flexible and affordable Subnet creation and management. The **Data API** supports a wide range of L1 blockchains (**over 100**) across both **mainnet** and **testnet**, including popular ones like Beam, DFK, Lamina1, Dexalot, Shrapnel, and Pulsar. In fact, every L1 you see on the [Avalanche Explorer](https://explorer.avax.network/) can be queried through the Data API. This list is continually expanding as we keep adding more L1s. For a full list of supported chains, visit [List chains](/docs/api-reference/data-api/evm-chains/supportedChains). #### The Contract Chain (C-Chain) The C-Chain is an implementation of the Ethereum Virtual Machine (EVM). The primary network endpoints only provide information related to C-Chain atomic memory balances and import/export transactions. For additional data, please reference the [EVM APIs](/docs/rpcs/c-chain/rpc). #### The Platform Chain (P-Chain) The P-Chain is responsible for all validator and L1-level operations. The P-Chain supports the creation of new blockchains and L1s, the addition of validators to L1s, staking operations, and other platform-level operations. #### The Exchange Chain (X-Chain) The X-Chain is responsible for operations on digital smart assets known as Avalanche Native Tokens. A smart asset is a representation of a real-world resource (for example, equity, or a bond) with sets of rules that govern its behavior, like "can’t be traded until tomorrow." The X-Chain supports the creation and trade of Avalanche Native Tokens. | Feature | Description | | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Chains** | Utilize this endpoint to retrieve the Primary Network chains that an address has transaction history associated with. | | **Blocks** | Blocks are the container for transactions executed on the Primary Network. Retrieve the latest blocks, a specific block by height or hash, or a list of blocks proposed by a specified NodeID on Primary Network chains. | | **Vertices** | Prior to Avalanche Cortina (v1.10.0), the X-Chain functioned as a DAG with vertices rather than blocks. These endpoints allow developers to retrieve historical data related to that period of chain history. Retrieve the latest vertices, a specific vertex, or a list of vertices at a specific height from the X-Chain. | | **Transactions** | Transactions are a user's primary form of interaction with a chain and provide details around their on-chain activity, including staking-related behavior. Retrieve a list of the latest transactions, a specific transaction, a list of active staking transactions for a specified address, or a list of transactions associated with a provided asset id from Primary Network chains. | | **UTXOs** | UTXOs are fundamental elements that denote the funds a user has available. Get a list of UTXOs for provided addresses from the Primary Network chains. | | **Balances** | User balances are an essential function of the blockchain. Retrieve balances related to the X and P-Chains, as well as atomic memory balances for the C-Chain. | | **Rewards** | Staking is the process where users lock up their tokens to support a blockchain network and, in return, receive rewards. It is an essential part of proof-of-stake (PoS) consensus mechanisms used by many blockchain networks, including Avalanche. Using the Data API, you can easily access pending and historical rewards associated with a set of addresses. | | **Assets** | Get asset details corresponding to the given asset id on the X-Chain. | #### EVM The C-Chain is an instance of the Coreth Virtual Machine, and many Avalanche L1s are instances of the *Subnet-EVM*, which is a Virtual Machine (VM) that defines the L1 Contract Chains. *Subnet-EVM* is a simplified version of *Coreth VM* (C-Chain). | Feature | Description | | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Chains** | There are a number of chains supported by the Data API. These endpoints can be used to understand which chains are included/indexed as part of the API and retrieve information related to a specific chain. | | **Blocks** | Blocks are the container for transactions executed within the EVM. Retrieve the latest blocks or a specific block by height or hash. | | **Transactions** | Transactions are a user's primary form of interaction with a chain and provide details around their on-chain activity. These endpoints can be used to retrieve information related to specific transaction details, internal transactions, contract deployments, specific token standard transfers, and more! | | **Balances** | User balances are an essential function of the blockchain. Easily retrieve native token, collectible, and fungible token balances related to an EVM chain with these endpoints. | #### Operations The Operations API allows users to easily access their on-chain history by creating transaction exports returned in a CSV format. This API supports EVMs as well as non-EVM Primary Network chains. # Rate Limits (/docs/api-reference/data-api/rate-limits) --- title: Rate Limits description: Rate Limits for the Data API icon: Clock --- Rate limiting is managed through a weighted scoring system, known as Compute Units (CUs). Each API request consumes a specified number of CUs, determined by the complexity of the request. This system is designed to accommodate basic requests while efficiently handling more computationally intensive operations. ## Rate Limit Tiers The maximum CUs (rate-limiting score) for a user depends on their subscription level and is delineated in the following table: | Subscription Level | Per Minute Limit (CUs) | Per Day Limit (CUs) | | :----------------- | :--------------------- | :------------------ | | Unauthenticated | 6,000 | 1,200,000 | | Free | 8,000 | 2,000,000 | | Base | 10,000 | 3,750,000 | | Growth | 14,000 | 11,200,000 | | Pro | 20,000 | 25,000,000 | To update your subscription level use the [AvaCloud Portal](https://app.avacloud.io/) Note: Rate limits apply collectively across both Webhooks and Data APIs, with usage from each counting toward your total CU limit. ## Rate Limit Categories The CUs for each category are defined in the following table: | Weight | CU Value | | :----- | :------- | | Free | 1 | | Small | 10 | | Medium | 20 | | Large | 50 | | XL | 100 | | XXL | 200 | ## Rate Limits for Data API Endpoints The CUs for each route are defined in the table below: | Endpoint | Method | Weight | CU Value | | :-------------------------------------------------------------------------------- | :----- | :----- | :------- | | `/v1/health-check` | GET | Medium | 20 | | `/v1/address/{address}/chains` | GET | Medium | 20 | | `/v1/transactions` | GET | Medium | 20 | | `/v1/blocks` | GET | Medium | 20 | | `/v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId}:reindex` | POST | Small | 10 | | `/v1/chains/{chainId}/nfts/collections/{address}/tokens` | GET | Medium | 20 | | `/v1/chains/{chainId}/nfts/collections/{address}/tokens/{tokenId}` | GET | Medium | 20 | | `/v1/operations/{operationId}` | GET | Small | 10 | | `/v1/operations/transactions:export` | POST | Medium | 20 | | `/v1/networks/{network}/blockchains/{blockchainId}/transactions/{txHash}` | GET | Medium | 20 | | `/v1/networks/{network}/blockchains/{blockchainId}/transactions` | GET | XL | 100 | | `/v1/networks/{network}/blockchains/{blockchainId}/transactions:listStaking` | GET | XL | 100 | | `/v1/networks/{network}/rewards:listPending` | GET | XL | 100 | | `/v1/networks/{network}/rewards` | GET | XL | 100 | | `/v1/networks/{network}/blockchains/{blockchainId}/utxos` | GET | XL | 100 | | `/v1/networks/{network}/blockchains/{blockchainId}/balances` | GET | XL | 100 | | `/v1/networks/{network}/blockchains/{blockchainId}/blocks/{blockId}` | GET | XL | 100 | | `/v1/networks/{network}/blockchains/{blockchainId}/nodes/{nodeId}/blocks` | GET | Medium | 20 | | `/v1/networks/{network}/blockchains/{blockchainId}/blocks` | GET | Medium | 20 | | `/v1/networks/{network}/blockchains/{blockchainId}/vertices` | GET | Medium | 20 | | `/v1/networks/{network}/blockchains/{blockchainId}/vertices/{vertexHash}` | GET | Medium | 20 | | `/v1/networks/{network}/blockchains/{blockchainId}/vertices:listByHeight` | GET | Medium | 20 | | `/v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId}` | GET | XL | 100 | | `/v1/networks/{network}/blockchains/{blockchainId}/assets/{assetId}/transactions` | GET | XL | 100 | | `/v1/networks/{network}/addresses:listChainIds` | GET | XL | 100 | | `/v1/networks/{network}` | GET | XL | 100 | | `/v1/networks/{network}/blockchains` | GET | Medium | 20 | | `/v1/networks/{network}/subnets` | GET | Medium | 20 | | `/v1/networks/{network}/subnets/{subnetId}` | GET | Medium | 20 | | `/v1/networks/{network}/validators` | GET | Medium | 20 | | `/v1/networks/{network}/validators/{nodeId}` | GET | Medium | 20 | | `/v1/networks/{network}/delegators` | GET | Medium | 20 | | `/v1/networks/{network}/l1Validators` | GET | Medium | 20 | | `/v1/teleporter/messages/{messageId}` | GET | Medium | 20 | | `/v1/teleporter/messages` | GET | Medium | 20 | | `/v1/teleporter/addresses/{address}/messages` | GET | Medium | 20 | | `/v1/icm/messages/{messageId}` | GET | Medium | 20 | | `/v1/icm/messages` | GET | Medium | 20 | | `/v1/icm/addresses/{address}/messages` | GET | Medium | 20 | | `/v1/apiUsageMetrics` | GET | XXL | 200 | | `/v1/apiLogs` | GET | XXL | 200 | | `/v1/subnetRpcUsageMetrics` | GET | XXL | 200 | | `/v1/rpcUsageMetrics` | GET | XXL | 200 | | `/v1/primaryNetworkRpcUsageMetrics` | GET | XXL | 200 | | `/v1/signatureAggregator/{network}/aggregateSignatures` | POST | Medium | 20 | | `/v1/signatureAggregator/{network}/aggregateSignatures/{txHash}` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/balances:getNative` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/balances:listErc20` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/balances:listErc721` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/balances:listErc1155` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/balances:listCollectibles` | GET | Medium | 20 | | `/v1/chains/{chainId}/blocks` | GET | Small | 10 | | `/v1/chains/{chainId}/blocks/{blockId}` | GET | Small | 10 | | `/v1/chains/{chainId}/contracts/{address}/transactions:getDeployment` | GET | Medium | 20 | | `/v1/chains/{chainId}/contracts/{address}/deployments` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}` | GET | Medium | 20 | | `/v1/chains` | GET | Free | 1 | | `/v1/chains/{chainId}` | GET | Free | 1 | | `/v1/chains/address/{address}` | GET | Free | 1 | | `/v1/chains/allTransactions` | GET | Free | 1 | | `/v1/chains/allBlocks` | GET | Free | 1 | | `/v1/chains/{chainId}/tokens/{address}/transfers` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/transactions` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/transactions:listNative` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/transactions:listErc20` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/transactions:listErc721` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/transactions:listErc1155` | GET | Medium | 20 | | `/v1/chains/{chainId}/addresses/{address}/transactions:listInternals` | GET | Medium | 20 | | `/v1/chains/{chainId}/transactions/{txHash}` | GET | Medium | 20 | | `/v1/chains/{chainId}/blocks/{blockId}/transactions` | GET | Medium | 20 | | `/v1/chains/{chainId}/transactions` | GET | Medium | 20 | ## Rate Limits for RPC endpoints The CUs for RPC calls are calculated based on the RPC method(s) within the request. The CUs assigned to each method are defined in the table below: | Method | Weight | CU Value | | :---------------------------------------- | :----- | :------- | | `eth_accounts` | Free | 1 | | `eth_blockNumber` | Small | 10 | | `eth_call` | Small | 10 | | `eth_coinbase` | Small | 10 | | `eth_chainId` | Free | 1 | | `eth_gasPrice` | Small | 10 | | `eth_getBalance` | Small | 10 | | `eth_getBlockByHash` | Small | 10 | | `eth_getBlockByNumber` | Small | 10 | | `eth_getBlockTransactionCountByNumber` | Medium | 20 | | `eth_getCode` | Medium | 20 | | `eth_getLogs` | XXL | 200 | | `eth_getStorageAt` | Medium | 20 | | `eth_getTransactionByBlockNumberAndIndex` | Medium | 20 | | `eth_getTransactionByHash` | Small | 10 | | `eth_getTransactionCount` | Small | 10 | | `eth_getTransactionReceipt` | Small | 10 | | `eth_signTransaction` | Medium | 20 | | `eth_sendTransaction` | Medium | 20 | | `eth_sign` | Medium | 20 | | `eth_sendRawTransaction` | Small | 10 | | `eth_syncing` | Free | 1 | | `net_listening` | Free | 1 | | `net_peerCount` | Medium | 20 | | `net_version` | Free | 1 | | `web3_clientVersion` | Small | 10 | | `web3_sha3` | Small | 10 | | `eth_newPendingTransactionFilter` | Medium | 20 | | `eth_maxPriorityFeePerGas` | Small | 10 | | `eth_baseFee` | Small | 10 | | `rpc_modules` | Free | 1 | | `eth_getChainConfig` | Small | 10 | | `eth_feeConfig` | Small | 10 | | `eth_getActivePrecompilesAt` | Small | 10 | All rate limits, weights, and CU values are subject to change. # Snowflake Datashare (/docs/api-reference/data-api/snowflake) --- title: Snowflake Datashare description: Snowflake Datashare for Avalanche blockchain data icon: Snowflake --- Avalanche Primary Network data (C-chain, P-chain, and X-chain blockchains) can be accessed in a sql-based table format via the [Snowflake Data Marketplace.](https://app.snowflake.com/marketplace) Explore the blockchain state since the Genesis Block. These tables provide insights on transaction gas fees, DeFi activity, the historical stake of validators on the primary network, AVAX emissions rewarded to past validators/delegators, and fees paid by Avalanche L1 Validators to the primary network. ## Available Blockchain Data #### Primary Network * **C-chain:** * Blocks * Transactions * Logs * Internal Transactions * Receipts * Messages * **P-chain:** * Blocks * Transactions * UTXOs * **X-chain:** * Blocks * Transactions * Vertices before the [X-chain Linearization](https://www.avax.network/blog/cortina-x-chain-linearization) in the Cortina Upgrade * **Dictionary:** A data dictionary is provided with the listing with column and table descriptions. Example columns include: * `c_blocks.blockchash` * `c_transactions.transactionfrom` * `c_logs.topichex_0` * `p_blocks.block_hash` * `p_blocks.block_index` * `p_blocks.type` * `p_transactions.timestamp` * `p_transactions.transaction_hash` * `utxos.utxo_id` * `utxos.address` * `vertices.vertex_hash` * `vertices.parent_hash` * `x_blocks.timestamp` * `x_blocks.proposer_id` * `x_transactions.transaction_hash` * `x_transactions.type` #### Available Avalanche L1s * **Gunzilla** * **Dexalot** * **DeFi Kingdoms (DFK)** * **Henesys (MapleStory Universe)** #### L1 Data * Blocks * Transactions * Logs * Internal Transactions (currently unavailable for DFK) * Receipts * Messages ## Access Search for "Ava Labs" on the [Snowflake Data Marketplace](https://app.snowflake.com/marketplace). # Usage Guide (/docs/api-reference/data-api/usage) --- title: Usage Guide description: Usage Guide for the Data API icon: Code --- ### Setup and Authentication In order to utilize your accounts rate limits, you will need to make API requests with an API key. You can generate API Keys from the AvaCloud portal. Once you've created and retrieved that, you will be able to make authenticated queries by passing in your API key in the `x-glacier-api-key` header of your HTTP request. An example curl request can be found below: ```bash curl -H "Content-Type: Application/json" -H "x-glacier-api-key: your_api_key" \ "https://glacier-api.avax.network/v1/chains" ``` ### Rate Limits The Data API has rate limits in place to maintain it's stability and protect from bursts of incoming traffic. The rate limits associated with various plans can be found within AvaCloud. When you hit your rate limit, the server will respond with a 429 http response code, and response headers to help you determine when you should start to make additional requests. The response headers follow the standards set in the RateLimit header fields for HTTP draft from the Internet Engineering Task Force. With every response with a valid api key, the server will include the following headers: * `ratelimit-policy` - The rate limit policy tied to your api key. * `ratelimit-limit` - The number of requests you can send according to your policy. * `ratelimit-remaining` - How many request remaining you can send in the period for your policy For any request after the rate limit has been reached, the server will also respond with these headers: * `ratelimit-reset` * `retry-after` Both of these headers are set to the number of seconds until your period is over and requests will start succeeding again. If you start receiving rate limit errors with the 429 response code, we recommend you discontinue sending requests to the server. You should wait to retry requests for the duration specified in the response headers. Alternatively, you can implement an exponential backoff algorithm to prevent continuous errors. Failure to discontinue requests may result in being temporarily blocked from accessing the API. Error Types The Data API generates standard error responses along with error codes based on provided requests and parameters. Typically, response codes within the `2XX` range signifies successful requests, while those within the `4XX` range points to errors originating from the client's side. Meanwhile, response codes within the `5XX` range indicates problems on the server's side. ### Error Types The Glacier API generates standard error responses along with error codes based on provided requests and parameters. Typically, response codes within the `2XX` range signifies successful requests, while those within the `4XX` range points to errors originating from the client's side. Meanwhile, response codes within the `5XX` range indicates problems on the server's side. The error response body is formatted like this: ```json { "message": ["Invalid address format"], // route specific error message "error": "Bad Request", // error type "statusCode": 400 // http response code } ``` Let's go through every error code that we can respond with: | Error Code | Error Type | Description | | :--------- | :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **400** | Bad Request | Bad requests generally mean the client has passed invalid or malformed parameters. Error messages in the response could help in evaluating the error. | | **401** | Unauthorized | When a client attempts to access resources that require authorization credentials but the client lacks proper authentication in the request, the server responds with 401. | | **403** | Forbidden | When a client attempts to access resources with valid credentials but doesn't have the privilege to perform that action, the server responds with 403. | | **404** | Not Found | The 404 error is mostly returned when the client requests with either mistyped URL, or the passed resource is moved or deleted, or the resource doesn't exist. | | **500** | Internal Server Error | The 500 error is a generic server-side error that is returned for any uncaught and unexpected issues on the server side. This should be very rare, and you may reach out to us if the problem persists for a longer duration. | | **502** | Bad Gateway | This is an internal error indicating invalid response received by the client-facing proxy or gateway from the upstream server. | | **503** | Service Unavailable | The 503 error is returned for certain routes on a particular Subnet. This indicates an internal problem with our Subnet node, and may not necessarily mean the Subnet is down or affected. | The above list is not exhaustive of all the errors that you'll receive, but is categorized on the basis of error codes. You may see route-specific errors along with detailed error messages for better evaluating the response. Reach out to our team when you see an error in the `5XX` range for a longer duration. These errors should be very rare, but we try to fix them as soon as possible once detected. ### Pagination When utilizing pagination for endpoints that return lists of data such as transactions, UTXOs, or blocks, our API uses a straightforward mechanism to manage the navigation through large datasets. We divide data into pages and each page is limited with a `pageSize` number of elements as passed in the request. Users can navigate to subsequent pages using the page token received in the `nextPageToken` field. This method ensures efficient retrieval. Routes with pagination have a following common response format: ```json { "blocks": [""], // This field name will vary by route "nextPageToken": "3d22deea-ea64-4d30-8a1e-c2a353b67e90" } ``` ### Page Token Structure * If there's more data in the dataset for the request, the API will include a UUID-based page token in the response. This token acts as a pointer to the next page of data. * The UUID page token is generated randomly and uniquely for each pagination scenario, enhancing security and minimizing predictability. * It's important to note that the page token is only returned when a next page is present. If there's no further data to retrieve, a page token will not be included in the response. * The generated page token has an expiration window of 24 hours. Beyond this timeframe, the token will no longer be valid for accessing subsequent pages. ### Integration and Usage: To make use of the pagination system, simply examine the API response. If a UUID page token is present, it indicates the availability of additional data on the next page. You can extract this token and include it in the subsequent request to access the subsequent page of results. Please note that you must ensure that the subsequent request is made within the 24-hour timeframe after the original token's generation. Beyond this duration, the token will expire, and you will need to initiate a fresh request from the initial page. By incorporating UUID page tokens, our API offers a secure, efficient, and user-friendly approach to navigating large datasets, streamlining your data retrieval proces ### Swagger API Reference You can explore the full API definitions and interact with the endpoints in the Swagger documentation at: [https://glacier-api.avax.network/api](https://glacier-api.avax.network/api) # Getting Started (/docs/api-reference/metrics-api/getting-started) --- title: Getting Started description: Getting Started with the Metrics API icon: Rocket --- The Metrics API is designed to be simple and accessible, requiring no authentication to get started. Just choose your endpoint, make your query, and instantly access on-chain data and analytics to power your applications. The following query retrieves the daily count of active addresses on the Avalanche C-Chain(43114) over the course of one month (from August 1, 2024 12:00:00 AM to August 31, 2024 12:00:00 AM), providing insights into user activity on the chain for each day during that period. With this data you can use JavaScript visualization tools like Chart.js, D3.js, Highcharts, Plotly.js, or Recharts to create interactive and insightful visual representations. ```bash curl --request GET \ --url 'https://metrics.avax.network/v2/chains/43114/metrics/activeAddresses?startTimestamp=1722470400&endTimestamp=1725062400&timeInterval=day&pageSize=31' ``` Response: ```json { "results": [ { "value": 37738, "timestamp": 1724976000 }, { "value": 53934, "timestamp": 1724889600 }, { "value": 58992, "timestamp": 1724803200 }, { "value": 73792, "timestamp": 1724716800 }, { "value": 70057, "timestamp": 1724630400 }, { "value": 46452, "timestamp": 1724544000 }, { "value": 46323, "timestamp": 1724457600 }, { "value": 73399, "timestamp": 1724371200 }, { "value": 52661, "timestamp": 1724284800 }, { "value": 52497, "timestamp": 1724198400 }, { "value": 50574, "timestamp": 1724112000 }, { "value": 46999, "timestamp": 1724025600 }, { "value": 45320, "timestamp": 1723939200 }, { "value": 54964, "timestamp": 1723852800 }, { "value": 60251, "timestamp": 1723766400 }, { "value": 48493, "timestamp": 1723680000 }, { "value": 71091, "timestamp": 1723593600 }, { "value": 50456, "timestamp": 1723507200 }, { "value": 46989, "timestamp": 1723420800 }, { "value": 50984, "timestamp": 1723334400 }, { "value": 46988, "timestamp": 1723248000 }, { "value": 66943, "timestamp": 1723161600 }, { "value": 64209, "timestamp": 1723075200 }, { "value": 57478, "timestamp": 1722988800 }, { "value": 80553, "timestamp": 1722902400 }, { "value": 70472, "timestamp": 1722816000 }, { "value": 53678, "timestamp": 1722729600 }, { "value": 70818, "timestamp": 1722643200 }, { "value": 99842, "timestamp": 1722556800 }, { "value": 76515, "timestamp": 1722470400 } ] } ``` Congratulations! You’ve successfully made your first query to the Metrics API. 🚀🚀🚀 # Metrics API (/docs/api-reference/metrics-api) --- title: Metrics API description: Access real-time and historical metrics for Avalanche networks icon: ChartLine --- ### What is the Metrics API? The Metrics API equips web3 developers with a robust suite of tools to access and analyze on-chain activity across Avalanche’s primary network, Avalanche L1s, and other supported EVM chains. This API delivers comprehensive metrics and analytics, enabling you to seamlessly integrate historical data on transactions, gas consumption, throughput, staking, and more into your applications. The Metrics API, along with the [Data API](/docs/api-reference/data-api) are the driving force behind every graph you see on the [Avalanche Explorer](https://explorer.avax.network/). From transaction trends to staking insights, the visualizations and data presented are all powered by these APIs, offering real-time and historical insights that are essential for building sophisticated, data-driven blockchain products.. ### Features * **Chain Throughput:** Retrieve detailed metrics on gas consumption, Transactions Per Second (TPS), and gas prices, including rolling windows of data for granular analysis. * **Cumulative Metrics:** Access cumulative data on addresses, contracts, deployers, and transaction counts, providing insights into network growth over time. * **Staking Information:** Obtain staking-related data, including the number of validators and delegators, along with their respective weights, across different subnets. * **Blockchains and Subnets:** Get information about supported blockchains, including EVM Chain IDs, blockchain IDs, and subnet associations, facilitating multi-chain analytics. * **Composite Queries:** Perform advanced queries by combining different metric types and conditions, enabling detailed and customizable data retrieval. The Metrics API is designed to provide developers with powerful tools to analyze and monitor on-chain activity across Avalanche’s primary network, Avalanche L1s, and other supported EVM chains. Below is an overview of the key features available: ### Chain Throughput Metrics * **Gas Consumption**
Track the average and maximum gas consumption per second, helping to understand network performance and efficiency. * **Transactions Per Second (TPS)**
Monitor the average and peak TPS to assess the network’s capacity and utilization. * **Gas Prices**
Analyze average and maximum gas prices over time to optimize transaction costs and predict fee trends. Monitor the average and peak TPS to assess the network’s capacity and utilization. ### Cumulative Metrics * **Address Growth**
Access the cumulative number of active addresses on a chain, providing insights into network adoption and user activity. * **Contract Deployment**
Monitor the cumulative number of smart contracts deployed, helping to gauge developer engagement and platform usage. * **Transaction Count**
Track the cumulative number of transactions, offering a clear view of network activity and transaction volume. ### Staking Information * **Validator and Delegator Counts**
Retrieve the number of active validators and delegators for a given L1, crucial for understanding network security and decentralization. * **Staking Weights**
Access the total stake weight of validators and delegators, helping to assess the distribution of staked assets across the network. ### Rolling Window Analytics * **Short-Term and Long-Term Metrics:** Perform rolling window analysis on various metrics like gas used, TPS, and gas prices, allowing for both short-term and long-term trend analysis. * **Customizable Time Frames:** Choose from different time intervals (hourly, daily, monthly) to suit your specific analytical needs. ### Blockchain and L1 Information * **Chain and L1 Mapping:** Get detailed information about EVM chains and their associated L1s, including chain IDs, blockchain IDs, and subnet IDs, facilitating cross-chain analytics. ### Advanced Composite Queries * **Custom Metrics Combinations**: Combine multiple metrics and apply logical operators to perform sophisticated queries, enabling deep insights and tailored analytics. * **Paginated Results:** Handle large datasets efficiently with paginated responses, ensuring seamless data retrieval in your applications. The Metrics API equips developers with the tools needed to build robust analytics, monitoring, and reporting solutions, leveraging the full power of multi-chain data across the Avalanche ecosystem and beyond. # Rate Limits (/docs/api-reference/metrics-api/rate-limits) --- title: Rate Limits description: Rate Limits for the Metrics API icon: Clock --- # Rate Limits Rate limiting is managed through a weighted scoring system, known as Compute Units (CUs). Each API request consumes a specified number of CUs, determined by the complexity of the request. This system is designed to accommodate basic requests while efficiently handling more computationally intensive operations. ## Rate Limit Tiers The maximum CUs (rate-limiting score) for a user depends on their subscription level and is delineated in the following table: | Subscription Level | Per Minute Limit (CUs) | Per Day Limit (CUs) | | :----------------- | :--------------------- | :------------------ | | Free | 8,000 | 1,200,000 | > We are working on new subscription tiers with higher rate limits to support even greater request volumes. ## Rate Limit Categories The CUs for each category are defined in the following table: | Weight | CU Value | | :----- | :------- | | Free | 1 | | Small | 20 | | Medium | 100 | | Large | 500 | | XL | 1000 | | XXL | 3000 | ## Rate Limits for Metrics Endpoints The CUs for each route are defined in the table below: | Endpoint | Method | Weight | CU Value | | :---------------------------------------------------------- | :----- | :----- | :------- | | `/v2/health-check` | GET | Free | 1 | | `/v2/chains` | GET | Free | 1 | | `/v2/chains/{chainId}` | GET | Free | 1 | | `/v2/chains/{chainId}/metrics/{metric}` | GET | Medium | 100 | | `/v2/chains/{chainId}/teleporterMetrics/{metric}` | GET | Medium | 100 | | `/v2/chains/{chainId}/rollingWindowMetrics/{metric}` | GET | Medium | 100 | | `/v2/networks/{network}/metrics/{metric}` | GET | Medium | 100 | | `/v2/chains/{chainId}/contracts/{address}/nfts:listHolders` | GET | Large | 500 | | `/v2/chains/{chainId}/contracts/{address}/balances` | GET | XL | 1000 | | `/v2/chains/43114/btcb/bridged:getAddresses` | GET | Large | 500 | | `/v2/subnets/{subnetId}/validators:getAddresses` | GET | Large | 500 | | `/v2/lookingGlass/compositeQuery` | POST | XXL | 3000 | All rate limits, weights, and CU values are subject to change. # Usage Guide (/docs/api-reference/metrics-api/usage-guide) --- title: Usage Guide description: Usage Guide for the Metrics API icon: Code --- The Metrics API does not require authentication, making it straightforward to integrate into your applications. You can start making API requests without the need for an API key or any authentication headers. #### Making Requests You can interact with the Metrics API by sending HTTP GET requests to the provided endpoints. Below is an example of a simple `curl` request. ```bash curl -H "Content-Type: Application/json" "https://metrics.avax.network/v1/avg_tps/{chainId}" ``` In the above request Replace `chainId` with the specific chain ID you want to query. For example, to retrieve the average transactions per second (TPS) for a specific chain (in this case, chain ID 43114), you can use the following endpoint: ```bash curl "https://metrics.avax.network/v1/avg_tps/43114" ``` The API will return a JSON response containing the average TPS for the specified chain over a series of timestamps and `lastRun` is a timestamp indicating when the last data point was updated: ```json { "results": [ {"timestamp": 1724716800, "value": 1.98}, {"timestamp": 1724630400, "value": 2.17}, {"timestamp": 1724544000, "value": 1.57}, {"timestamp": 1724457600, "value": 1.82}, // Additional data points... ], "status": 200, "lastRun": 1724780812 } ``` ### Rate Limits Even though the Metrics API does not require authentication, it still enforces rate limits to ensure stability and performance. If you exceed these limits, the server will respond with a 429 Too Many Requests HTTP response code. ### Error Types The API generates standard error responses along with error codes based on provided requests and parameters. Typically, response codes within the `2XX` range signifies successful requests, while those within the `4XX` range points to errors originating from the client's side. Meanwhile, response codes within the `5XX` range indicates problems on the server's side. The error response body is formatted like this: ```json { "message": ["Invalid address format"], // route specific error message "error": "Bad Request", // error type "statusCode": 400 // http response code } ``` Let's go through every error code that we can respond with: | Error Code | Error Type | Description | | ---------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **400** | Bad Request | Bad requests generally mean the client has passed invalid or malformed parameters. Error messages in the response could help in evaluating the error. | | **401** | Unauthorized | When a client attempts to access resources that require authorization credentials but the client lacks proper authentication in the request, the server responds with 401. | | **403** | Forbidden | When a client attempts to access resources with valid credentials but doesn't have the privilege to perform that action, the server responds with 403. | | **404** | Not Found | The 404 error is mostly returned when the client requests with either mistyped URL, or the passed resource is moved or deleted, or the resource doesn't exist. | | **500** | Internal Server Error | The 500 error is a generic server-side error that is returned for any uncaught and unexpected issues on the server side. This should be very rare, and you may reach out to us if the problem persists for a longer duration. | | **502** | Bad Gateway | This is an internal error indicating invalid response received by the client-facing proxy or gateway from the upstream server. | | **503** | Service Unavailable | The 503 error is returned for certain routes on a particular Subnet. This indicates an internal problem with our Subnet node, and may not necessarily mean the Subnet is down or affected. | ### Pagination For endpoints that return large datasets, the Metrics API employs pagination to manage the results. When querying for lists of data, you may receive a nextPageToken in the response, which can be used to request the next page of data. Example response with pagination: ```json { "results": [...], "nextPageToken": "3d22deea-ea64-4d30-8a1e-c2a353b67e90" } ``` To retrieve the next set of results, include the nextPageToken in your subsequent request: ```bash curl -H "Content-Type: Application/json" \ "https://metrics.avax.network/v1/avg_tps/{chainId}?pageToken=3d22deea-ea64-4d30-8a1e-c2a353b67e90" ``` ### Pagination Details #### Page Token Structure The `nextPageToken` is a UUID-based token provided in the response when additional pages of data are available. This token serves as a pointer to the next set of data. * **UUID Generation**: The `nextPageToken` is generated uniquely for each pagination scenario, ensuring security and ensuring predictability. * **Expiration**: The token is valid for 24 hours from the time it is generated. After this period, the token will expire, and a new request starting from the initial page will be required. * **Presence**: The token is only included in the response when there is additional data available. If no more data exists, the token will not be present. #### Integration and Usage To use the pagination system effectively: * Check if the `nextPageToken` is present in the response. * If present, include this token in the subsequent request to fetch the next page of results. * Ensure that the follow-up request is made within the 24-hour window after the token was generated to avoid token expiration. By utilizing the pagination mechanism, you can efficiently manage and navigate through large datasets, ensuring a smooth data retrieval process. ### Swagger API Reference You can explore the full API definitions and interact with the endpoints in the Swagger documentation at: [https://metrics.avax.network/api](https://metrics.avax.network/api) # Webhooks API (/docs/api-reference/webhook-api) --- title: Webhooks API description: Real-time notifications for blockchain events on Avalanche networks icon: Webhook --- ### What is the Webhooks API? The Webhooks API lets you monitor real-time events on the Avalanche ecosystem, including the C-chain, L1s, and the Platform Chain (P/X chains). By subscribing to specific events, you can receive instant notifications for on-chain occurrences without continuously polling the network. webhooks ### Key Features: * **Real-time notifications:** Receive immediate updates on specified on-chain activities without polling. * **Customizable:** Specify the desired event type to listen for, customizing notifications based on your individual requirements. * **Secure:** Employ shared secrets and signature-based verification to ensure that notifications originate from a trusted source. * **Broad Coverage:** * **C-chain:** Mainnet and testnet, covering smart contract events, NFT transfers, and wallet-to-wallet transactions. * **Platform Chain (P and X chains):** Address and validator events, staking activities, and other platform-level transactions. By supporting both the C-chain and the Platform Chain, you can monitor an even wider range of Avalanche activities. ### Use cases * **NFT marketplace transactions**: Get alerts for NFT minting, transfers, auctions, bids, sales, and other interactions within NFT marketplaces. * **Wallet notifications**: Receive alerts when an address performs actions such as sending, receiving, swapping, or burning assets. * **DeFi activities**: Receive notifications for various DeFi activities such as liquidity provisioning, yield farming, borrowing, lending, and liquidations. * **Staking rewards:** Get real-time notifications when a validator stakes, receives delegation, or earns staking rewards on the P-Chain, enabling seamless monitoring of validator earnings and participation. ## APIs for continuous polling vs. Webhooks for events data The following example uses the address activity webhook topic to illustrate the difference between polling an API for wallet event data versus subscribing to a webhook topic to receive wallet events. webhooks ### Continous polling Continuous polling is a method where your application repeatedly sends requests to an API at fixed intervals to check for new data or events. Think of it like checking your mailbox every five minutes to see if new mail has arrived, whether or not anything is there. * You want to track new transactions for a specific wallet. * Your application calls an API every few seconds (e.g., every 5 seconds) with a query like, “Are there any new transactions for this wallet since my last check?” * The API responds with either new transaction data or a confirmation that nothing has changed. **Downsides of continuous polling** * **Inefficiency:** Your app makes requests even when no new transactions occur, wasting computational resources, bandwidth, and potentially incurring higher API costs. For example, if no transactions happen for an hour, your app still sends hundreds of unnecessary requests. * **Delayed updates:** Since polling happens at set intervals, there’s a potential delay in detecting events. If a transaction occurs just after a poll, your app won’t know until the next check—up to 5 seconds later in our example. This lag can be critical for time-sensitive applications, like trading or notifications. * **Scalability challenges:** Monitoring one wallet might be manageable, but if you’re tracking dozens or hundreds of wallets, the number of requests multiplies quickly. ### Webhook subscription Webhooks are an event-driven alternative where your application subscribes to specific events, and the Avalanche service notifies you instantly when those events occur. It’s like signing up for a delivery alert—when the package (event) arrives, you get a text message right away, instead of checking the tracking site repeatedly. * Your app registers a webhook specifying an endpoint (e.g., `https://your-app.com/webhooks/transactions`) and the event type (e.g., `address_activity`). * When a new transaction occurs we send a POST request to your endpoint with the transaction details. * Your app receives the data only when something happens, with no need to ask repeatedly. **Benefits of Avalanche webhooks** * **Real-Time updates:** Notifications arrive the moment a transaction is processed, eliminating delays inherent in polling. This is ideal for applications needing immediate responses, like alerting users or triggering automated actions. * **Efficiency:** Your app doesn’t waste resources making requests when there’s no new data. Data flows only when events occur. This reduces server load, bandwidth usage, and API call quotas. * **Scalability:** You can subscribe to events for multiple wallets or event types (e.g., transactions, smart contract calls) without increasing the number of requests your app makes. We handle the event detection and delivery, so your app scales effortlessly as monitoring needs grow. ## Event payload structure The Event structure always begins with the following parameters: ```json theme={null} { "webhookId": "6d1bd383-aa8d-47b5-b793-da6d8a115fde", "eventType": "address_activity", "messageId": "8e4e7284-852a-478b-b425-27631c8d22d2", "event": { } } ``` **Parameters:** * `webhookId`: Unique identifier for the webhook in your account. * `eventType`: The event that caused the webhook to be triggered. In the future, there will be multiple types of events, for the time being only the address\_activity event is supported. The address\_activity event gets triggered whenever the specified addresses participate in a token or AVAX transaction. * `messageId`: Unique identifier per event sent. * `event`: Event payload. It contains details about the transaction, logs, and traces. By default logs and internal transactions are not included, if you want to include them use `"includeLogs": true`, and `"includeInternalTxs": true`. ### Address Activity webhook The address activity webhook allows you to track any interaction with an address (any address). Here is an example of this type of event: ```json theme={null} { "webhookId": "263942d1-74a4-4416-aeb4-948b9b9bb7cc", "eventType": "address_activity", "messageId": "94df1881-5d93-49d1-a1bd-607830608de2", "event": { "transaction": { "blockHash": "0xbd093536009f7dd785e9a5151d80069a93cc322f8b2df63d373865af4f6ee5be", "blockNumber": "44568834", "from": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7", "gas": "651108", "gasPrice": "31466275484", "maxFeePerGas": "31466275484", "maxPriorityFeePerGas": "31466275484", "txHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4", "txStatus": "1", "input": "0xb80c2f090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e000000000000000000000000000000000000000000000000006ca0c737b131f2000000000000000000000000000000000000000000000000000000000011554e000000000000000000000000000000000000000000000000000000006627dadc0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000006ca0c737b131f2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c70000000000000000000000000000000000000000000000000000000000000001000000000000000000000000be882fb094143b59dc5335d32cecb711570ebdd40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000be882fb094143b59dc5335d32cecb711570ebdd400000000000000000000000000000000000000000000000000000000000000010000000000000000000027100e663593657b064e1bae76d28625df5d0ebd44210000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e0000000000000000000000000000000000000000000000000000000000000bb80000000000000000000000000000000000000000000000000000000000000000", "nonce": "4", "to": "0x1dac23e41fc8ce857e86fd8c1ae5b6121c67d96d", "transactionIndex": 0, "value": "30576074978046450", "type": 0, "chainId": "43114", "receiptCumulativeGasUsed": "212125", "receiptGasUsed": "212125", "receiptEffectiveGasPrice": "31466275484", "receiptRoot": "0xf355b81f3e76392e1b4926429d6abf8ec24601cc3d36d0916de3113aa80dd674", "erc20Transfers": [ { "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4", "type": "ERC20", "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4", "value": "30576074978046450", "blockTimestamp": 1713884373, "logIndex": 2, "erc20Token": { "address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "valueWithDecimals": "0.030576074978046448" } }, { "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4", "type": "ERC20", "from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421", "to": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7", "value": "1195737", "blockTimestamp": 1713884373, "logIndex": 3, "erc20Token": { "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "name": "USD Coin", "symbol": "USDC", "decimals": 6, "valueWithDecimals": "1.195737" } }, { "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4", "type": "ERC20", "from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4", "to": "0x0E663593657B064e1baE76d28625Df5D0eBd4421", "value": "30576074978046450", "blockTimestamp": 1713884373, "logIndex": 4, "erc20Token": { "address": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "valueWithDecimals": "0.030576074978046448" } } ], "erc721Transfers": [], "erc1155Transfers": [], "internalTransactions": [ { "from": "0xf73166f0c75a3DF444fAbdFDC7e5EE4a73fA51C7", "to": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "internalTxType": "CALL", "value": "30576074978046450", "gasUsed": "212125", "gasLimit": "651108", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xF2781Bb34B6f6Bb9a6B5349b24de91487E653119", "internalTxType": "DELEGATECALL", "value": "30576074978046450", "gasUsed": "176417", "gasLimit": "605825", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "internalTxType": "STATICCALL", "value": "0", "gasUsed": "9750", "gasLimit": "585767", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6", "internalTxType": "DELEGATECALL", "value": "0", "gasUsed": "2553", "gasLimit": "569571", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "internalTxType": "CALL", "value": "30576074978046450", "gasUsed": "23878", "gasLimit": "566542", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "internalTxType": "CALL", "value": "0", "gasUsed": "25116", "gasLimit": "540114", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4", "internalTxType": "CALL", "value": "0", "gasUsed": "81496", "gasLimit": "511279", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4", "to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "internalTxType": "STATICCALL", "value": "0", "gasUsed": "491", "gasLimit": "501085", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4", "to": "0x0E663593657B064e1baE76d28625Df5D0eBd4421", "internalTxType": "CALL", "value": "0", "gasUsed": "74900", "gasLimit": "497032", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421", "to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "internalTxType": "CALL", "value": "0", "gasUsed": "32063", "gasLimit": "463431", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6", "internalTxType": "DELEGATECALL", "value": "0", "gasUsed": "31363", "gasLimit": "455542", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421", "to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "internalTxType": "STATICCALL", "value": "0", "gasUsed": "2491", "gasLimit": "430998", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421", "to": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4", "internalTxType": "CALL", "value": "0", "gasUsed": "7591", "gasLimit": "427775", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0xbe882fb094143B59Dc5335D32cEcB711570EbDD4", "to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "internalTxType": "CALL", "value": "0", "gasUsed": "6016", "gasLimit": "419746", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x0E663593657B064e1baE76d28625Df5D0eBd4421", "to": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", "internalTxType": "STATICCALL", "value": "0", "gasUsed": "491", "gasLimit": "419670", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "internalTxType": "STATICCALL", "value": "0", "gasUsed": "3250", "gasLimit": "430493", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6", "internalTxType": "DELEGATECALL", "value": "0", "gasUsed": "2553", "gasLimit": "423121", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0x1daC23e41Fc8ce857E86fD8C1AE5b6121C67D96d", "to": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "internalTxType": "STATICCALL", "value": "0", "gasUsed": "1250", "gasLimit": "426766", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" }, { "from": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", "to": "0x30DFE0469803BcE76F8F62aC24b18d33D3d6FfE6", "internalTxType": "DELEGATECALL", "value": "0", "gasUsed": "553", "gasLimit": "419453", "transactionHash": "0xf6a791920652e87ccc91d2f1b20c1505a94452b88f359acdeb5a6fa8205638c4" } ], "blockTimestamp": 1713884373 } } } ``` # Rate Limits (/docs/api-reference/webhook-api/rate-limits) --- title: Rate Limits description: Rate Limits for the Webhooks API icon: Clock --- Rate limiting is managed through a weighted scoring system, known as Compute Units (CUs). Each API request consumes a specified number of CUs, determined by the complexity of the request. This system is designed to accommodate basic requests while efficiently handling more computationally intensive operations. ## Rate Limit Tiers The maximum CUs (rate-limiting score) for a user depends on their subscription level and is delineated in the following table: | Subscription Level | Per Minute Limit (CUs) | Per Day Limit (CUs) | | :----------------- | :--------------------- | :------------------ | | Unauthenticated | 6,000 | 1,200,000 | | Free | 8,000 | 2,000,000 | | Base | 10,000 | 3,750,000 | | Growth | 14,000 | 11,200,000 | | Pro | 20,000 | 25,000,000 | To update your subscription level use the [AvaCloud Portal](https://app.avacloud.io/) Note: Rate limits apply collectively across both Webhooks and Data APIs, with usage from each counting toward your total CU limit. ## Rate Limit Categories The CUs for each category are defined in the following table: | Weight | CU Value | | :----- | :------- | | Free | 1 | | Small | 10 | | Medium | 20 | | Large | 50 | | XL | 100 | | XXL | 200 | ## Rate Limits for Webhook Endpoints The CUs for each route are defined in the table below: | Endpoint | Method | Weight | CU Value | | :------------------------------------------ | :----- | :----- | :------- | | `/v1/webhooks` | POST | Medium | 20 | | `/v1/webhooks` | GET | Small | 10 | | `/v1/webhooks/{id}` | GET | Small | 10 | | `/v1/webhooks/{id}` | DELETE | Medium | 20 | | `/v1/webhooks/{id}` | PATCH | Medium | 20 | | `/v1/webhooks:generateOrRotateSharedSecret` | POST | Medium | 20 | | `/v1/webhooks:getSharedSecret` | GET | Small | 10 | | `/v1/webhooks/{id}/addresses` | PATCH | Medium | 20 | | `/v1/webhooks/{id}/addresses` | DELETE | Medium | 20 | | `/v1/webhooks/{id}/addresses` | GET | Medium | 20 | All rate limits, weights, and CU values are subject to change. # Retry mechanism (/docs/api-reference/webhook-api/retries) --- title: Retry mechanism description: Retry mechanism for the Webhook API icon: RotateCcw --- Our webhook system is designed to ensure you receive all your messages, even if temporary issues prevent immediate delivery. To achieve this, we’ve implemented a retry mechanism that resends messages if they don’t get through on the first attempt. Importantly, **retries are handled on a per-message basis**, meaning each webhook message follows its own independent retry schedule. This ensures that the failure of one message doesn’t affect the delivery attempts of others. This guide will walk you through how the retry mechanism works, the differences between free and paid tier users, and practical steps you can take to ensure your system handles webhooks effectively. ## How it works When we send a webhook message to your server, we expect a `200` status code within 10 seconds to confirm successful receipt. Your server should return this response immediately and process the message afterward. Processing the message before sending the response can lead to timeouts and trigger unnecessary retries. webhooks * **Attempt 1:** We send the message expecting a respose with `200` status code. If we do not receive a `200` status code within **10 seconds**, the attempt is considered failed. During this window, any non-`2xx` responses are ignored. * **Attempt 2:** Occurs **10 seconds** after the first attempt, with another 10-second timeout and the same rule for ignoring non-`2xx` responses. * **Retry Queue After Two Failed Attempts** If both initial attempts fail, the message enters a **retry queue** with progressively longer intervals between attempts. Each retry attempt still has a 10-second timeout, and non-`2xx` responses are ignored during this window. The retry schedule is as follows: | Attempt | Interval | | ------- | -------- | | 3 | 1 min | | 4 | 5 min | | 5 | 10 min | | 6 | 30 min | | 7 | 2 hours | | 8 | 6 hours | | 9 | 12 hours | | 10 | 24 hours | **Total Retry Duration:** Up to approximately 44.8 hours (2,688 minutes) if all retries are exhausted. **Interval Timing:** Each retry interval starts 10 seconds after the previous attempt is deemed failed. For example, if attempt 2 fails at t=20 seconds, attempt 3 will start at t=80 seconds (20s + 1 minute interval + 10s). Since retries are per message, multiple messages can be in different stages of their retry schedules simultaneously without interfering with each other. ## Differences Between Free and Paid Tier Users The behavior of the retry mechanism varies based on your subscription tier: **Free tier users** * **Initial attempts limit:** If six messages fail both the first and second attempts, your webhook will be automatically deactivated. * **Retry queue limit:** Only five messages can enter the retry queue over the lifetime of the subscription. If a sixth message requires retry queuing, or if any message fails all 10 retry attempts, the subscription will be deactivated. **Paid tier users** * For paid users, webhooks will be deactivated if a single message, retried at the 24-hour interval, fails to process successfully. ## What you can do **Ensure server availability:** * Keep your server running smoothly to receive webhook messages without interruption. * Implement logging for incoming webhook requests and your server's responses to help identify any issues quickly. **Design for idempotency** * Set up your webhook handler so it can safely process the same message multiple times without causing errors or unwanted effects. This way, if retries occur, they won't negatively impact your system. * The webhook retry mechanism is designed to maximize the reliability of message delivery while minimizing the impact of temporary issues. By understanding how retries work—especially the per-message nature of the system—and following best practices like ensuring server availability and designing for idempotency, you can ensure a seamless experience with webhooks. ## Key Takeaways * Each message has its own retry schedule, ensuring isolation and reliability. * Free tier users have limits on failed attempts and retry queue entries, while paid users do not. * Implement logging and idempotency to handle retries effectively and avoid disruptions. * By following this guide, you’ll be well-equipped to manage webhooks and ensure your system remains robust, even in the face of temporary challenges. # Webhook Signature (/docs/api-reference/webhook-api/webhooks-signature) --- title: Webhook Signature description: Webhook Signature for the Webhook API icon: Signature --- To make your webhooks extra secure, you can verify that they originated from our side by generating an HMAC SHA-256 hash code using your Authentication Token and request body. You can get the signing secret through the AvaCloud portal or Glacier API. ### Find your signing secret **Using the portal**\ Navigate to the webhook section and click on Generate Signing Secret. Create the secret and copy it to your code. **Using Data API**\ The following endpoint retrieves a shared secret: ```bash curl --location 'https://glacier-api.avax.network/v1/webhooks:getSharedSecret' \ --header 'x-glacier-api-key: ' \ ``` ### Validate the signature received Every outbound request will include an authentication signature in the header. This signature is generated by: 1. **Canonicalizing the JSON Payload**: This means arranging the JSON data in a standard format. 2. **Generating a Hash**: Using the HMAC SHA256 hash algorithm to create a hash of the canonicalized JSON payload. To verify that the signature is from us, follow these steps: 1. Generate the HMAC SHA256 hash of the received JSON payload. 2. Compare this generated hash with the signature in the request header. This process, known as verifying the digital signature, ensures the authenticity and integrity of the request. **Example Request Header** ``` Content-Type: application/json; x-signature: your-hashed-signature ``` ### Example Signature Validation Function This Node.js code sets up an HTTP server using the Express framework. It listens for POST requests sent to the `/callback` endpoint. Upon receiving a request, it validates the signature of the request against a predefined `signingSecret`. If the signature is valid, it logs match; otherwise, it logs no match. The server responds with a JSON object indicating that the request was received. ### Node (JavaScript) ```javascript const express = require('express'); const crypto = require('crypto'); const { canonicalize } = require('json-canonicalize'); const app = express(); app.use(express.json({limit: '50mb'})); const signingSecret = 'c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53'; function isValidSignature(signingSecret, signature, payload) { const canonicalizedPayload = canonicalize(payload); const hmac = crypto.createHmac('sha256', Buffer.from(signingSecret, 'hex')); const digest = hmac.update(canonicalizedPayload).digest('base64'); console.log("signature: ", signature); console.log("digest", digest); return signature === digest; } app.post('/callback', express.json({ type: 'application/json' }), (request, response) => { const { body, headers } = request; const signature = headers['x-signature']; // Handle the event switch (body.evenType) { case 'address\*activity': console.log("\*\** Address*activity \*\*\*"); console.log(body); if (isValidSignature(signingSecret, signature, body)) { console.log("match"); } else { console.log("no match"); } break; // ... handle other event types default: console.log(`Unhandled event type ${body}`); } // Return a response to acknowledge receipt of the event response.json({ received: true }); }); const PORT = 8000; app.listen(PORT, () => console.log(`Running on port ${PORT}`)); ``` ### Python (Flask) ```python from flask import Flask, request, jsonify import hmac import hashlib import base64 import json app = Flask(__name__) SIGNING_SECRET = 'c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53' def canonicalize(payload): """Function to canonicalize JSON payload""" # In Python, canonicalization can be achieved by using sort_keys=True in json.dumps return json.dumps(payload, separators=(',', ':'), sort_keys=True) def is_valid_signature(signing_secret, signature, payload): canonicalized_payload = canonicalize(payload) hmac_obj = hmac.new(bytes.fromhex(signing_secret), canonicalized_payload.encode('utf-8'), hashlib.sha256) digest = base64.b64encode(hmac_obj.digest()).decode('utf-8') print("signature:", signature) print("digest:", digest) return signature == digest @app.route('/callback', methods=['POST']) def callback_handler(): body = request.json signature = request.headers.get('x-signature') # Handle the event if body.get('eventType') == 'address_activity': print("*** Address_activity ***") print(body) if is_valid_signature(SIGNING_SECRET, signature, body): print("match") else: print("no match") else: print(f"Unhandled event type {body}") # Return a response to acknowledge receipt of the event return jsonify({"received": True}) if __name__ == '__main__': PORT = 8000 print(f"Running on port {PORT}") app.run(port=PORT) ``` ### Go (net/http) ```go package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "net/http" "sort" "strings" ) const signingSecret = "c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53" // Canonicalize function sorts the JSON keys and produces a canonicalized string func Canonicalize(payload map[string]interface{}) (string, error) { var sb strings.Builder var keys []string for k := range payload { keys = append(keys, k) } sort.Strings(keys) sb.WriteString("{") for i, k := range keys { v, err := json.Marshal(payload[k]) if err != nil { return "", err } sb.WriteString(fmt.Sprintf("\"%s\":%s", k, v)) if i < len(keys)-1 { sb.WriteString(",") } } sb.WriteString("}") return sb.String(), nil } func isValidSignature(signingSecret, signature string, payload map[string]interface{}) bool { canonicalizedPayload, err := Canonicalize(payload) if err != nil { fmt.Println("Error canonicalizing payload:", err) return false } key, err := hex.DecodeString(signingSecret) if err != nil { fmt.Println("Error decoding signing secret:", err) return false } h := hmac.New(sha256.New, key) h.Write([]byte(canonicalizedPayload)) digest := h.Sum(nil) encodedDigest := base64.StdEncoding.EncodeToString(digest) fmt.Println("signature:", signature) fmt.Println("digest:", encodedDigest) return signature == encodedDigest } func callbackHandler(w http.ResponseWriter, r *http.Request) { var body map[string]interface{} err := json.NewDecoder(r.Body).Decode(&body) if err != nil { fmt.Println("Error decoding body:", err) http.Error(w, "Invalid request body", http.StatusBadRequest) return } signature := r.Header.Get("x-signature") eventType, ok := body["eventType"].(string) if !ok { fmt.Println("Error parsing eventType") http.Error(w, "Invalid event type", http.StatusBadRequest) return } switch eventType { case "address_activity": fmt.Println("*** Address_activity ***") fmt.Println(body) if isValidSignature(signingSecret, signature, body) { fmt.Println("match") } else { fmt.Println("no match") } default: fmt.Printf("Unhandled event type %s\n", eventType) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]bool{"received": true}) } func main() { http.HandleFunc("/callback", callbackHandler) fmt.Println("Running on port 8000") http.ListenAndServe(":8000", nil) } ``` ### Rust (actix-web) ```rust use actix_web::{web, App, HttpServer, HttpResponse, Responder, post}; use serde::Deserialize; use hmac::{Hmac, Mac}; use sha2::Sha256; use base64::encode; use std::collections::BTreeMap; type HmacSha256 = Hmac; const SIGNING_SECRET: &str = "c13cc017c4ed63bcc842c8edfb49df37512280326a32826de3b885340b8a3d53"; #[derive(Deserialize)] struct EventPayload { eventType: String, // Add other fields as necessary } // Canonicalize the JSON payload by sorting keys fn canonicalize(payload: &BTreeMap) -> String { serde_json::to_string(payload).unwrap() } fn is_valid_signature(signing_secret: &str, signature: &str, payload: &BTreeMap) -> bool { let canonicalized_payload = canonicalize(payload); let mut mac = HmacSha256::new_from_slice(signing_secret.as_bytes()) .expect("HMAC can take key of any size"); mac.update(canonicalized_payload.as_bytes()); let result = mac.finalize(); let digest = encode(result.into_bytes()); println!("signature: {}", signature); println!("digest: {}", digest); digest == signature } #[post("/callback")] async fn callback(body: web::Json>, req: web::HttpRequest) -> impl Responder { let signature = req.headers().get("x-signature").unwrap().to_str().unwrap(); if let Some(event_type) = body.get("eventType").and_then(|v| v.as_str()) { match event_type { "address_activity" => { println!("*** Address_activity ***"); println!("{:?}", body); if is_valid_signature(SIGNING_SECRET, signature, &body) { println!("match"); } else { println!("no match"); } } _ => { println!("Unhandled event type: {}", event_type); } } } else { println!("Error parsing eventType"); return HttpResponse::BadRequest().finish(); } HttpResponse::Ok().json(serde_json::json!({ "received": true })) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .service(callback) }) .bind("0.0.0.0:8000")? .run() .await } ``` ### TypeScript (ChainKit SDK) ```typescript import { isValidSignature } from '@avalanche-sdk/chainkit/utils'; import express from 'express'; const app = express(); app.use(express.json()); const signingSecret = 'your-signing-secret'; // Replace with your signing secret app.post('/webhook', (req, res) => { const signature = req.headers['x-signature']; const payload = req.body; if (isValidSignature(signingSecret, signature, payload)) { console.log('Valid signature'); // Process the request } else { console.log('Invalid signature'); } res.json({ received: true }); }); app.listen(8000, () => console.log('Server running on port 8000')); ``` # WebSockets vs Webhooks (/docs/api-reference/webhook-api/wss-vs-webhooks) --- title: WebSockets vs Webhooks description: WebSockets vs Webhooks for the Webhook API icon: GitCompare --- Reacting to real-time events from Avalanche smart contracts allows for immediate responses and automation, improving user experience and streamlining application functionality. It ensures that applications stay synchronized with the blockchain state. There are two primary methods for receiving these on-chain events: * **WebSockets**, using libraries like Ethers.js or Viem * **Webhooks**, which send structured event data directly to your app via HTTP POST. Both approaches enable real-time interactions, but they differ drastically in their reliability, ease of implementation, and long-term maintainability. In this post, we break down why Webhooks are the better, more resilient choice for most Avalanche developers. ## Architecture Overview The diagram below compares the two models side-by-side: wss_vs_webhooks **WebSockets** * The app connects to the Avalanche RPC API over WSS to receive raw log data. * It must decode logs, manage connection state, and store data locally. * On disconnection, it must re-sync via an external Data API or using standard `eth_*` RPC calls (e.g., `eth_getLogs`, `eth_getBlockByNumber`). Important: WSS is a transport protocol—not real-time by itself. Real-time capabilities come from the availability of `eth_subscribe`, which requires node support. **Webhooks** * The app exposes a simple HTTP endpoint. * Decoded event data is pushed directly via POST, including token metadata. * Built-in retries ensure reliable delivery, even during downtime. Important: Webhooks have a 48-hour retry window. If your app is down for longer, you still need a re-sync strategy using `eth_*` calls to recover older missed events. *** ## Using WebSockets: Real-time but high maintenance WebSockets allow you to subscribe to events using methods like eth\_subscribe. These subscriptions notify your app in real-time whenever new logs, blocks, or pending transactions meet your criteria. ```javascript import { createPublicClient, webSocket, formatUnits } from 'viem'; import { avalancheFuji } from 'viem/chains'; import { usdcAbi } from './usdc-abi.mjs'; // Ensure this includes the Transfer event // Your wallet address (case-insensitive comparison) const MY_WALLET = '0x8ae323046633A07FB162043f28Cea39FFc23B50A'.toLowerCase(); //Chrome async function monitorTransfers() { try { // USDC.e contract address on Avalanche Fuji const usdcAddress = '0x5425890298aed601595a70AB815c96711a31Bc65'; // Set up the WebSocket client for Avalanche Fuji const client = createPublicClient({ chain: avalancheFuji, transport: webSocket('wss://api.avax-test.network/ext/bc/C/ws'), }); // Watch for Transfer events on the USDC contract client.watchContractEvent({ address: usdcAddress, abi: usdcAbi, eventName: 'Transfer', onLogs: (logs) => { logs.forEach((log) => { const { from, to, value } = log.args; const fromLower = from.toLowerCase(); // Filter for transactions where 'from' matches your wallet if (fromLower === MY_WALLET) { console.log('*******'); console.log('Transfer from my wallet:'); console.log(`From: ${from}`); console.log(`To: ${to}`); console.log(`Value: ${formatUnits(value, 6)} USDC`); // USDC has 6 decimals console.log(`Transaction Hash: ${log.transactionHash}`); } }); }, onError: (error) => { console.error('Event watching error:', error.message); }, }); console.log('Monitoring USDC Transfer events on Fuji...'); } catch (error) { console.error('Error setting up transfer monitoring:', error.message); } } // Start monitoring monitorTransfers(); ``` The downside? If your connection drops, you lose everything in between. You’ll need to: * Set up a database to track the latest processed block and log index. * Correctly handling dropped connections and reconnection by hand can be challenging to get right. * Use `eth_getLogs` to re-fetch missed logs. * Decode and process raw logs yourself to rebuild app state. This requires extra infrastructure, custom recovery logic, and significant maintenance overhead. *** ## Webhooks: Resilient and developer-friendly Webhooks eliminate the complexity of managing live connections. Instead, you register an HTTP endpoint to receive blockchain event payloads when they occur. Webhook payload example: ```json { "eventType": "address_activity", "event": { "transaction": { "txHash": "0x1d8f...", "from": "0x3D3B...", "to": "0x9702...", "erc20Transfers": [ { "valueWithDecimals": "110.56", "erc20Token": { "symbol": "USDt", "decimals": 6 } } ] } } } ``` You get everything you need: * Decoded event data * Token metadata (name, symbol, decimals) * Full transaction context * No extra calls. No parsing. No manual re-sync logic. *** ## Key Advantages of Webhooks * **Reliable delivery with zero effort:** Built-in retries ensure no missed events during downtime * **Instant enrichment:** Payloads contain decoded logs, token metadata, and transaction context * **No extra infrastructure:** No WebSocket connections, no DB, no external APIs * **Faster development:** Go from idea to production with fewer moving parts * **Lower operational cost:** Less compute, fewer network calls, smaller surface area to manage If we compare using a table: | Feature | WebSockets (Ethers.js/Viem) | Webhooks | | | | :----------------------------- | :------------------------------------------------- | :--------------------------------------------------- | - | - | | **Interruption Handling** | Manual; Requires complex custom logic | Automatic; Built-in queues & retries | | | | **Data Recovery** | Requires DB + External API for re-sync | Handled by provider; No re-sync logic needed | | | | **Dev Complexity** | High; Error-prone custom resilience code | Low; Focus on processing incoming POST data | | | | **Infrastructure** | WSS connection + DB + Potential Data API cost | Application API endpoint | | | | **Data Integrity** | Risk of gaps if recovery logic fails | High; Ensures eventual delivery | | | | **Payload** | Often raw; Requires extra calls for context | Typically enriched and ready-to-use | | | | **Multiple addresses** | Manual filtering or separate listeners per address | Supports direct configuration for multiple addresses | | | | **Listen to wallet addresses** | Requires manual block/transaction filtering | Can monitor wallet addresses and smart contracts | | | ## Summary * WebSockets offer real-time access to Avalanche data, but come with complexity: raw logs, reconnect logic, re-sync handling, and decoding responsibilities. * Webhooks flip the model: the data comes to you, pre-processed and reliable. You focus on your product logic instead of infrastructure. * If you want to ship faster, operate more reliably, and reduce overhead, Webhooks are the better path forward for Avalanche event monitoring. # Chain Components (/docs/builderkit/components/chains) --- title: Chain Components description: "Components for displaying and selecting blockchain networks." --- # Chain Components Chain components help you manage network selection and display chain information. ## ChainIcon The ChainIcon component displays chain logos. ```tsx import { ChainIcon } from '@avalabs/builderkit'; // Basic usage ``` ### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID to display | | `className` | `string` | - | Additional CSS classes | ## ChainDropdown The ChainDropdown component provides network selection functionality. ```tsx import { ChainDropdown } from '@avalabs/builderkit'; // Basic usage { console.log('Selected chain:', chainId); }} /> ``` ### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `selected` | `number` | - | Currently selected chain ID | | `list` | `number[]` | - | List of available chain IDs | | `onSelectionChanged` | `(chain_id: number) => void` | - | Selection change callback | | `className` | `string` | - | Additional CSS classes | ## ChainRow The ChainRow component displays detailed chain information. ```tsx import { ChainRow } from '@avalabs/builderkit'; // Basic usage ``` ### Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID | | `name` | `string` | - | Chain name | | `className` | `string` | - | Additional CSS classes | # Control Components (/docs/builderkit/components/control) --- title: Control Components description: "Interactive control components like buttons and wallet connection interfaces." --- # Control Components Control components provide interactive elements for your Web3 application. ## Button The Button component is a versatile control that supports multiple states and actions. ```tsx import { Button } from '@avalabs/builderkit'; // Basic usage ``` Run the project using Nodejs. ```bash npm install express axios path body-parser dotenv node app.js ``` Open a Chrome tab and type `http://localhost:3000`, you should see something like this. Then click on Connect and accept receiving push notifications. If you are using MacOS, check in **System Settings** > **Notifications** that you have enabled notifications for the browser. If everything runs correctly your browser should be registered in OneSignal. To check go to **Audience** > **Subscriptions** and verify that your browser is registered. ### Step 3 - Backend Setup Now, let's configure the backend to manage webhook events and dispatch notifications based on the incoming data. Here's the step-by-step process: 1. **Transaction Initiation:** When someone starts a transaction with your wallet as the destination, the webhooks detect the transaction and generate an event. 2. **Event Triggering:** The backend receives the event triggered by the transaction, containing the destination address. 3. **ExternalID Retrieval:** Using the received address, the backend retrieves the corresponding `externalID` associated with that wallet. 4. **Notification Dispatch:** The final step involves sending a notification through OneSignal, utilizing the retrieved `externalID`. OneSignal Backend #### 3.1 - Use Ngrok to tunnel the traffic to localhost If we want to test the webhook in our computer and we are behind a proxy/NAT device or a firewall we need a tool like Ngrok. Glacier will trigger the webhook and make a POST to the Ngrok cloud, then the request is forwarded to your local Ngrok client who in turn forwards it to the Node.js app listening on port 3000. Go to [https://ngrok.com/](https://ngrok.com/) create a free account, download the binary, and connect to your account. Create a Node.js app with Express and paste the following code to receive the webhook: To start an HTTP tunnel forwarding to your local port 3000 with Ngrok, run this next: ```bash ./ngrok http 3000 ``` You should see something like this: ``` ngrok (Ctrl+C to quit) Take our ngrok in production survey! https://forms.gle/aXiBFWzEA36DudFn6 Session Status online Account javier.toledo@avalabs.org (Plan: Free) Version 3.8.0 Region United States (us) Latency 48ms Web Interface http://127.0.0.1:4040 Forwarding https://c902-2600-1700-5220-11a0-813c-d5ac-d72c-f7fd.ngrok-free.app -> http://localhost:3000 Connections ttl opn rt1 rt5 p50 p90 33 0 0.00 0.00 5.02 5.05 HTTP Requests ------------- ``` #### 3.2 - Create the webhook The webhook can be created using the [Avacloud Dashboard](https://app.avacloud.io/) or Glacier API. For convenience, we are going to use cURL. For that copy the forwarding URL generated by Ngrok and append the `/callbackpath` and our address. ```bash curl --location 'https://glacier-api-dev.avax.network/v1/webhooks' \ --header 'x-glacier-api-key: ' \ --header 'Content-Type: application/json' \ --data '{ "url": " https://c902-2600-1700-5220-11a0-813c-d5ac-d72c-f7fd.ngrok-free.app/callback", "chainId": "43113", "eventType": "address_activity", "includeInternalTxs": true, "includeLogs": true, "metadata": { "addresses": ["0x8ae323046633A07FB162043f28Cea39FFc23B50A"] }, "name": "My wallet", "description": "My wallet" }' ``` Don't forget to add your API Key. If you don't have one go to the [Avacloud Dashboard](https://app.avacloud.io/) and create a new one. #### 3.3 - The backend To run the backend we need to add the environment variables in the root of your project. For that create an `.env` file with the following values: ``` PORT=3000 ONESIGNAL_API_KEY= APP_ID= ``` To get the APP ID from OneSignal go to **Settings** > **Keys and IDs** Since we are simulating the connection to a database to retrieve the externalID, we need to add the wallet address and the OneSignal externalID to the myDB array. ```javascript //simulating a DB const myDB = [ { name: 'wallet1', address: '0x8ae323046633A07FB162043f28Cea39FFc23B50A', externalID: '9c96e91d40c7a44c763fb55960e12293afbcfaf6228860550b0c1cc09cd40ac3' }, { name: 'wallet2', address: '0x1f83eC80D755A87B31553f670070bFD897c40CE0', externalID: '0xd39d39c99305c6df2446d5cc3d584dc1eb041d95ac8fb35d4246f1d2176bf330' } ]; ``` The code handles a webhook event triggered when a wallet receives a transaction, performs a lookup in the simulated "database" using the receiving address to retrieve the corresponding OneSignal `externalID`, and then sends an instruction to OneSignal to dispatch a notification to the browser, with OneSignal ultimately delivering the web push notification to the browser. ```javascript require('dotenv').config(); const axios = require('axios'); const express = require('express'); const bodyParser = require('body-parser'); const path = require('path'); const app = express(); const port = process.env.PORT || 3000; // Serve static website app.use(bodyParser.json()); app.use(express.static(path.join(__dirname, './client'))); //simulating a DB const myDB = [ { name: 'wallet1', address: '0x8ae323046633A07FB162043f28Cea39FFc23B50A', externalID: '9c96e91d40c7a44c763fb55960e12293afbcfaf6228860550b0c1cc09cd40ac3' }, { name: 'wallet2', address: '0x1f83eC80D755A87B31553f670070bFD897c40CE0', externalID: '0xd39d39c99305c6df2446d5cc3d584dc1eb041d95ac8fb35d4246f1d2176bf330' } ]; app.post('/callback', async (req, res) => { const { body } = req; try { res.sendStatus(200); handleTransaction(body.event.transaction).catch(error => { console.error('Error processing transaction:', error); }); } catch (error) { console.error('Error processing transaction:', error); res.status(500).json({ error: 'Internal server error' }); } }); // Handle transaction async function handleTransaction(transaction) { console.log('*****Transaction:', transaction); const notifications = []; const erc20Transfers = transaction?.erc20Transfers || []; for (const transfer of erc20Transfers) { const externalID = await getExternalID(transfer.to); const { symbol, valueWithDecimals } = transfer.erc20Token; notifications.push({ type: transfer.type, sender: transfer.from, receiver: transfer.to, amount: valueWithDecimals, token: symbol, externalID }); } if (transaction?.networkToken) { const { tokenSymbol, valueWithDecimals } = transaction.networkToken; const externalID = await getExternalID(transaction.to); notifications.push({ sender: transaction.from, receiver: transaction.to, amount: valueWithDecimals, token: tokenSymbol, externalID }); } if (notifications.length > 0) { sendNotifications(notifications); } } //connect to DB and return externalID async function getExternalID(address) { const entry = myDB.find(entry => entry.address.toLowerCase() === address.toLowerCase()); return entry ? entry.externalID : null; } // Send notifications async function sendNotifications(notifications) { for (const notification of notifications) { try { const data = { include_aliases: { external_id: [notification.externalID.toLowerCase()] }, target_channel: 'push', isAnyWeb: true, contents: { en: `You've received ${notification.amount} ${notification.token}` }, headings: { en: 'Core wallet' }, name: 'Notification', app_id: process.env.APP_ID }; console.log('data:', data); const response = await axios.post('https://onesignal.com/api/v1/notifications', data, { headers: { Authorization: `Bearer ${process.env.ONESIGNAL_API_KEY}`, 'Content-Type': 'application/json' } }); console.log('Notification sent:', response.data); } catch (error) { console.error('Error sending notification:', error); // Optionally, implement retry logic here } } } // Start the server app.listen(port, () => { console.log(`App listening at http://localhost:${port}`); }); ``` You can now start your backend server by running: ```shell node app.js ``` Send AVAX from another wallet to the wallet being monitored by the webhook and you should receive a notification with the amount of Avax received. You can try it with any other ERC20 token as well. ### Conclusion In this tutorial, we've set up a frontend to connect to the Core wallet and enable push notifications using OneSignal. We've also implemented a backend to handle webhook events and send notifications based on the received data. By integrating the frontend with the backend, users can receive real-time notifications for blockchain events. # ChainDropdown (/docs/builderkit/components/chains/chain-dropdown) --- title: ChainDropdown description: "A dropdown component for selecting Avalanche chains with visual chain information." --- # ChainDropdown The ChainDropdown component provides a styled dropdown menu for selecting between different Avalanche chains. ## Usage ```tsx import { ChainDropdown } from '@avalabs/builderkit'; // Basic usage console.log('Selected chain:', chainId)} /> // With custom styling ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `selected` | `number` | - | Currently selected chain ID | | `list` | `number[]` | - | Array of available chain IDs | | `onSelectionChanged` | `(chain_id: number) => void` | - | Callback when selection changes | | `className` | `string` | - | Additional CSS classes | ## Features - Displays chain information using ChainRow component - Maintains selected state internally - Styled dropdown with Tailwind CSS - Uses common Select components for consistent UI - Automatic chain information lookup ## Examples ### Basic Chain Selection ```tsx ``` ### With Network Switching ```tsx { try { await switchNetwork(chainId); } catch (error) { console.error('Failed to switch network:', error); } }} /> ``` ### Custom Styling ```tsx ``` ### With Chain Filtering ```tsx supportedChains.includes(id))} onSelectionChanged={handleChainChange} /> ``` ## Component Structure The dropdown consists of: 1. **Trigger**: Shows currently selected chain 2. **Content**: List of available chains 3. **Items**: Individual chain rows with icons and names ## Visual States 1. **Default**: Shows selected chain 2. **Open**: Displays list of available chains 3. **Hover**: Highlights chain under cursor 4. **Selected**: Indicates current selection ## Chain Information The component uses the `useChains` hook to fetch chain information, which includes: - Chain ID - Chain name - Chain icon (via ChainRow component) ## Styling Default styling includes: - Primary background color - Contrasting text color - Rounded corners - Proper padding and spacing - Hover and focus states # ChainIcon (/docs/builderkit/components/chains/chain-icon) --- title: ChainIcon description: "A component for displaying chain logos based on chain ID." --- # ChainIcon The ChainIcon component displays chain logos for Avalanche networks using a standardized path structure. ## Usage ```tsx import { ChainIcon } from '@avalabs/builderkit'; // Basic usage // With custom styling ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID to display logo for | | `className` | `string` | - | Additional CSS classes | ## Features - Displays chain logos from a standardized path structure - Uses common Icon component for consistent display - Supports custom styling through className - Simple and lightweight implementation ## Examples ### Basic Chain Icon ```tsx ``` ### Custom Size ```tsx ``` ### In a List ```tsx
``` ### With Border ```tsx ``` ## Asset Requirements The component expects chain logo images to be available at: ``` /chains/logo/{chain_id}.png ``` For example: ``` /chains/logo/43114.png // Avalanche C-Chain /chains/logo/43113.png // Fuji Testnet ``` # ChainRow (/docs/builderkit/components/chains/chain-row) --- title: ChainRow description: "A component for displaying chain information in a row layout with icon and name." --- # ChainRow The ChainRow component displays chain information in a horizontal layout, combining a chain icon with its name. ## Usage ```tsx import { ChainRow } from '@avalabs/builderkit'; // Basic usage // With custom styling ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID | | `name` | `string` | - | Chain name | | `className` | `string` | - | Additional CSS classes | ## Features - Combines ChainIcon with chain name - Horizontal layout with proper spacing - Flexible styling through className - Simple and lightweight implementation - Consistent alignment and spacing ## Examples ### Basic Chain Display ```tsx ``` ### In a List ```tsx
``` ### Interactive Row ```tsx ``` ### With Custom Styling ```tsx ``` ## Layout Structure The component uses a flex layout with: - ChainIcon on the left - Chain name on the right - Gap between icon and name - Center alignment of items # Collectible (/docs/builderkit/components/collectibles/collectible) --- title: Collectible description: "A component for displaying NFT collectibles with metadata and image support." --- # Collectible The Collectible component displays NFT collectibles with automatic metadata resolution and image display. ## Usage ```tsx import { Collectible } from '@avalabs/builderkit'; // Basic usage // With custom styling ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID where the NFT exists | | `address` | `string` | - | NFT contract address | | `token_id` | `number` | - | Token ID of the NFT | | `className` | `string` | - | Additional CSS classes | ## Features - Automatic metadata resolution from IPFS - Displays NFT image and name - Shows token ID - Supports ERC721 standard - Responsive layout with fixed dimensions - Loading state handling ## Examples ### Basic NFT Display ```tsx ``` ### In a Grid Layout ```tsx
{nfts.map(nft => ( ))}
``` ### With Custom Styling ```tsx ``` ## Component Structure 1. **Container** - Fixed width of 120px - Rounded corners - Border - Overflow hidden 2. **Image** - Fixed dimensions (120x120) - Maintains aspect ratio - Centered display 3. **Info Section** - White background - Token ID display - NFT name - Proper padding ## Metadata Resolution The component automatically: 1. Fetches token URI from the contract 2. Resolves IPFS metadata 3. Extracts image URL and name 4. Handles IPFS gateway resolution # Button (/docs/builderkit/components/control/button) --- title: Button description: "A versatile button component that supports multiple states and actions." --- # Button The Button component is a versatile control that supports multiple states and actions. ## Usage ```tsx import { Button } from '@avalabs/builderkit'; // Basic usage ``` ## Validation States 1. **Initial**: No validation indicator 2. **Valid**: No visual feedback (clean state) 3. **Invalid**: Red ring around input 4. **Disabled**: Grayed out appearance # AmountInput (/docs/builderkit/components/input/amount-input) --- title: AmountInput description: "A specialized input component for handling numeric amounts with proper formatting." --- # AmountInput The AmountInput component provides a specialized input field for handling numeric amounts with automatic formatting and validation. ## Usage ```tsx import { AmountInput } from '@avalabs/builderkit'; import { DollarSign } from 'lucide-react'; // Basic usage console.log('Amount:', value)} /> // With currency icon } onChange={handleAmountChange} /> ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `type` | `string` | - | Input type (usually "text") | | `placeholder` | `string` | - | Placeholder text | | `value` | `string` | `""` | Controlled input value | | `disabled` | `boolean` | `false` | Whether the input is disabled | | `icon` | `ReactNode` | - | Optional icon element | | `onChange` | `(value: string) => void` | - | Called with formatted amount | | `className` | `string` | - | Additional CSS classes | ## Features - Automatic number formatting - Prevents invalid number inputs - Handles decimal points appropriately - Optional icon support - Controlled value management - Background color inheritance ## Examples ### Basic Amount Input ```tsx ``` ### With Maximum Value ```tsx { if (parseFloat(value) <= maxAmount) { setAmount(value); } }} /> ``` ### With Currency Symbol ```tsx $} onChange={handleAmount} className="text-right" /> ``` ### In a Form ```tsx
``` ## Number Formatting The component uses `parseNumberInput` utility to: - Allow only numeric input - Handle decimal points - Remove leading zeros - Maintain proper number format # Input (/docs/builderkit/components/input/input) --- title: Input description: "A base input component with icon support and controlled value management." --- # Input The Input component provides a base text input with support for icons, controlled values, and custom styling. ## Usage ```tsx import { Input } from '@avalabs/builderkit'; import { Search } from 'lucide-react'; // Basic usage console.log('Input value:', value)} /> // With icon } onChange={handleSearch} /> // Disabled state ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `type` | `string` | - | Input type (e.g., 'text', 'number') | | `placeholder` | `string` | - | Placeholder text | | `value` | `any` | `""` | Controlled input value | | `disabled` | `boolean` | `false` | Whether the input is disabled | | `icon` | `ReactNode` | - | Optional icon element | | `onChange` | `(value: any) => void` | - | Value change callback | | `className` | `string` | - | Additional CSS classes | ## Features - Controlled input value management - Optional icon support - Disabled state handling - Flexible styling with Tailwind CSS - Consistent padding and spacing - Background inheritance for seamless integration ## Examples ### Text Input ```tsx ``` ### Search Input ```tsx } onChange={handleSearch} className="bg-gray-100" /> ``` ### Number Input ```tsx ``` ### With Validation ```tsx ``` ## Layout Structure The component uses a flex container with: - Optional icon on the left - Full-width input field - Consistent padding and gap - Background color inheritance # MultiChainTokenInput (/docs/builderkit/components/input/multi-chain-token-input) --- title: MultiChainTokenInput description: "A token selection component that supports tokens across multiple chains." --- # MultiChainTokenInput The MultiChainTokenInput component provides a token selection interface that allows users to select tokens from different chains. ## Usage ```tsx import { MultiChainTokenInput } from '@avalabs/builderkit'; // Basic usage console.log('Selected token:', token)} showBalances={true} /> ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `selected` | `{ address: string, chain_id: number } & Partial` | - | Currently selected token with chain | | `list` | `TokenItem[]` | - | Array of tokens across all chains | | `onSelectionChanged` | `(token: TokenItem) => void` | - | Called when token selection changes | | `showBalances` | `boolean` | - | Whether to show token balances | | `className` | `string` | - | Additional CSS classes | ## Features - Token selection across multiple chains - Chain selection interface - Displays token with chain icon - Shows token balances (optional) - Searchable token list per chain - Responsive dialog design ## Examples ### Basic Multi-Chain Selection ```tsx ``` ### With Balances ```tsx ``` ### In a Cross-Chain Form ```tsx
t.chain_id !== sourceToken.chain_id)} onSelectionChanged={setDestinationToken} showBalances={true} />
``` ### Custom Styling ```tsx ``` ## Component Structure 1. **Trigger** - TokenChip with chain icon - Chevron down indicator - Click to open dialog 2. **Dialog** - Header with back button - Chain selection row - Search input - Chain-specific token list - Token balances (if enabled) ## Chain Selection The component automatically extracts unique chain IDs from the token list and displays them as selectable options: ```tsx let chains = Array.from(new Set(list.map(t => t.chain_id))); ``` # TokenInput (/docs/builderkit/components/input/token-input) --- title: TokenInput description: "A token selection component with a searchable token list and balance display." --- # TokenInput The TokenInput component provides a token selection interface with a modal dialog containing a searchable list of tokens. ## Usage ```tsx import { TokenInput } from '@avalabs/builderkit'; // Basic usage console.log('Selected token:', token)} showBalances={true} /> ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `selected` | `{ address: string } & Partial` | - | Currently selected token | | `chain_id` | `number` | - | Chain ID for the token list | | `list` | `TokenItem[]` | - | Array of available tokens | | `onSelectionChanged` | `(token: TokenItem) => void` | - | Called when token selection changes | | `showBalances` | `boolean` | - | Whether to show token balances | | `className` | `string` | - | Additional CSS classes | ## Features - Token selection through modal dialog - Displays token icon and symbol - Shows token balances (optional) - Searchable token list - Uses TokenChip for selected token display - Responsive dialog design ## Examples ### Basic Token Selection ```tsx ``` ### With Balances ```tsx ``` ### In a Form ```tsx
t.address !== fromToken.address)} onSelectionChanged={setToToken} showBalances={true} />
``` ### Custom Styling ```tsx ``` ## Component Structure 1. **Trigger** - TokenChip showing selected token - Chevron down indicator - Click to open dialog 2. **Dialog** - Header with back button - Search input - Scrollable token list - Token balances (if enabled) ## Token Item Structure ```tsx type TokenItem = { chain_id: number; address: string; name: string; symbol: string; balance?: BigNumber; } ``` # TokenChip (/docs/builderkit/components/tokens/token-chip) --- title: TokenChip description: "A compact component for displaying token information with optional chain icon and copy functionality." --- # TokenChip The TokenChip component provides a compact way to display token information, including token icon, name, symbol, and optional chain information. ## Usage ```tsx import { TokenChip } from '@avalabs/builderkit'; // Basic usage // With chain icon and copy functionality ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID of the token | | `address` | `string` | - | Token contract address | | `symbol` | `string` | - | Token symbol | | `name` | `string` | - | Token name (optional) | | `showChainIcon` | `boolean` | `false` | Whether to show chain icon alongside token icon | | `showName` | `boolean` | `true` | Whether to show token name (if false, shows only symbol) | | `allowCopyToClipboard` | `boolean` | `false` | Enable copy address to clipboard functionality | | `className` | `string` | - | Additional CSS classes | ## Features - Displays token icon with optional chain icon - Shows token name and/or symbol - Copy to clipboard functionality with visual feedback - Flexible layout options - Tailwind CSS styling support ## Examples ### Basic Token Display ```tsx ``` ### With Chain Icon ```tsx ``` ### Symbol Only ```tsx ``` ### With Copy Functionality ```tsx ``` ### Custom Styling ```tsx ``` ## Visual States 1. **Default**: Shows token information with icon 2. **Copy Button**: Shows copy icon when `allowCopyToClipboard` is true 3. **Copy Confirmation**: Shows check icon briefly after copying 4. **Chain Display**: Shows chain icon when `showChainIcon` is true # TokenIconWithChain (/docs/builderkit/components/tokens/token-icon-with-chain) --- title: TokenIconWithChain description: "A component for displaying token logos with an overlaid chain icon." --- # TokenIconWithChain The TokenIconWithChain component displays a token logo with its corresponding chain icon overlaid in the bottom-right corner. ## Usage ```tsx import { TokenIconWithChain } from '@avalabs/builderkit'; // Basic usage // With custom styling ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID of the token | | `address` | `string` | - | Token contract address | | `className` | `string` | - | Additional CSS classes (applied to both icons) | ## Features - Combines TokenIcon with chain logo - Chain icon is automatically positioned and scaled - Supports custom styling through className - Uses consistent icon paths for both token and chain logos - Responsive layout with Tailwind CSS ## Examples ### Basic Usage ```tsx ``` ### Custom Size ```tsx ``` ### In a Token List ```tsx
``` ### With Custom Border ```tsx ``` ## Asset Requirements The component requires two image assets: 1. Token logo at: ``` /tokens/logo/{chain_id}/{address}.png ``` 2. Chain logo at: ``` /chains/logo/{chain_id}.png ``` For example: ``` /tokens/logo/43114/0x1234567890123456789012345678901234567890.png /chains/logo/43114.png ``` ## Layout Details The component uses the following layout structure: - Main token icon as the primary element - Chain icon positioned at bottom-right - Chain icon scaled to 50% of the token icon size - Chain icon has a white border for visual separation # TokenIcon (/docs/builderkit/components/tokens/token-icon) --- title: TokenIcon description: "A component for displaying token logos." --- # TokenIcon The TokenIcon component displays token logos based on the token's chain ID and contract address. ## Usage ```tsx import { TokenIcon } from '@avalabs/builderkit'; // Basic usage // With custom styling ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID of the token | | `address` | `string` | - | Token contract address | | `className` | `string` | - | Additional CSS classes | ## Features - Displays token logos from a standardized path structure - Supports custom styling through className - Uses common Icon component for consistent display - Follows `/tokens/logo/{chain_id}/{address}.png` path convention ## Examples ### Basic Token Icon ```tsx ``` ### Custom Size ```tsx ``` ### In a List ```tsx
``` ### With Border ```tsx ``` ## Asset Requirements The component expects token logo images to be available at: ``` /tokens/logo/{chain_id}/{address}.png ``` For example: ``` /tokens/logo/43114/0x1234567890123456789012345678901234567890.png ``` # TokenList (/docs/builderkit/components/tokens/token-list) --- title: TokenList description: "A searchable list component for displaying and selecting tokens with optional balance information." --- # TokenList The TokenList component provides a searchable list of tokens with support for balance display and token selection. ## Usage ```tsx import { TokenList } from '@avalabs/builderkit'; // Basic usage console.log('Selected token:', address)} /> // With balances and selected token ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID for the token list | | `list` | `TokenItem[]` | - | Array of tokens to display | | `onClick` | `(address: string) => void` | - | Callback when a token is selected | | `selected` | `{ address: string }` | - | Currently selected token (optional) | | `showBalances` | `boolean` | `false` | Whether to show token balances | | `className` | `string` | - | Additional CSS classes | ## Features - Search by token name, symbol, or contract address - Displays token balances (optional) - Highlights selected token - Supports token whitelisting - Responsive scrollable list - Search input with clear functionality ## Examples ### Basic Token List ```tsx ``` ### With Balances ```tsx ``` ### With Selected Token ```tsx ``` ## Token Item Structure Each token in the list should follow this structure: ```tsx type TokenItem = { chain_id: number; address: string; name: string; symbol: string; balance?: BigNumber; // Optional, used when showBalances is true whitelisted?: boolean; // Optional, for token verification } ``` ## States 1. **Loading**: Shows loading state when fetching balances 2. **Empty Search**: Displays all tokens in the list 3. **Search Results**: Shows filtered tokens based on search input 4. **Selected**: Highlights the currently selected token 5. **Non-whitelisted**: Shows warning for non-whitelisted tokens ## Search Functionality The component supports searching by: - Token name - Token symbol - Exact contract address When searching by contract address: - Must be a valid Ethereum address - Shows token details if found in the list - Can show import option for non-whitelisted tokens # TokenRow (/docs/builderkit/components/tokens/token-row) --- title: TokenRow description: "A component for displaying token information in a row layout with optional balance display." --- # TokenRow The TokenRow component displays token information in a row layout, combining a TokenChip with optional balance information. ## Usage ```tsx import { TokenRow } from '@avalabs/builderkit'; // Basic usage // With balance and click handler handleTokenSelect()} /> ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID of the token | | `address` | `string` | - | Token contract address | | `name` | `string` | - | Token name | | `symbol` | `string` | - | Token symbol | | `balance` | `BigNumber` | - | Token balance (optional) | | `onClick` | `() => void` | - | Click handler (optional) | | `className` | `string` | - | Additional CSS classes | ## Features - Displays token information using TokenChip - Shows token balance with 3 decimal places - Supports click interactions - Flexible styling with Tailwind CSS - Responsive layout with proper alignment ## Examples ### Basic Display ```tsx ``` ### With Balance ```tsx ``` ### Interactive Row ```tsx selectToken("AVAX")} className="hover:bg-gray-100 cursor-pointer" /> ``` ### In a List ```tsx
``` ## Layout Structure The component uses a flex layout with: - Left side: TokenChip (icon, name, and symbol) - Right side (if balance provided): - Token balance (3 decimal places) - USD value (currently hardcoded to $0) - Proper spacing and alignment - Optional hover and click interactions # TransactionButton (/docs/builderkit/components/transaction/transaction-button) --- title: TransactionButton description: "A button component that handles blockchain transaction submission with built-in status tracking and notifications." --- # TransactionButton The TransactionButton component handles individual transaction submission with built-in status tracking and notifications. ## Usage ```tsx import { TransactionButton } from '@avalabs/builderkit'; // Basic usage // With callbacks { console.log('Transaction sent at:', timestamp); }} onTransactionConfirmed={(receipt) => { console.log('Transaction confirmed:', receipt); }} /> ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID for the transaction | | `title` | `string` | - | Transaction title | | `description` | `string` | - | Transaction description | | `data` | `any` | - | Transaction data | | `onTransactionSent` | `(timestamp: number) => void` | - | Called when transaction is sent | | `onTransactionConfirmed` | `(receipt: any) => void` | - | Called when transaction is confirmed | | `className` | `string` | - | Additional CSS classes | ## Features - Automatic wallet connection handling - Network switching support - Transaction status tracking - Toast notifications with explorer links - Loading states and error handling ## Examples ### Basic Transaction ```tsx ``` ### With Custom Styling ```tsx ``` ## Component States 1. **Not Connected** - Shows "Connect Wallet" button - Handles wallet connection 2. **Wrong Network** - Shows "Wrong Network" button - Handles network switching 3. **Ready** - Shows transaction button - Enables transaction submission 4. **Processing** - Shows loading indicator - Tracks transaction status ## Notifications The component provides toast notifications for: - Transaction sent - Transaction confirmed - Transaction failed Each notification includes: - Timestamp - Transaction explorer link - Appropriate styling # TransactionManager (/docs/builderkit/components/transaction/transaction-manager) --- title: TransactionManager description: "A component that orchestrates multiple blockchain transactions in sequence." --- # TransactionManager The TransactionManager component orchestrates multiple blockchain transactions in sequence, handling the flow between steps and providing status tracking. ## Usage ```tsx import { TransactionManager } from '@avalabs/builderkit'; // Basic usage { console.log('Step completed at:', timestamp); }} onTransactionConfirmed={(receipt) => { console.log('All transactions completed:', receipt); }} /> ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `chain_id` | `number` | - | Chain ID for the transactions | | `transactions` | `TransactionProps[]` | - | Array of transactions to process | | `onTransactionSent` | `(timestamp: number) => void` | - | Called when a step completes | | `onTransactionConfirmed` | `(receipt: any) => void` | - | Called when all transactions complete | | `className` | `string` | - | Additional CSS classes | ## Features - Sequential transaction execution - Step-by-step progress tracking - Automatic state management - Transaction dependency handling - Consistent error handling - Status notifications for each step ## Examples ### Token Approval and Transfer ```tsx ``` ### Multi-step Protocol Interaction ```tsx ``` ## Transaction Flow 1. **Initialization** - Validates transaction array - Sets up initial state - Prepares first transaction 2. **Step Execution** - Processes current transaction - Waits for confirmation - Updates progress state 3. **Step Transition** - Validates completion - Moves to next transaction - Updates UI state 4. **Completion** - Confirms all steps finished - Triggers completion callback - Resets internal state # Avalanche L1 Configs (/docs/nodes/chain-configs/avalanche-l1s/avalanche-l1-configs) --- title: "Avalanche L1 Configs" description: "This page describes the configuration options available for Avalanche L1s." edit_url: https://github.com/ava-labs/avalanchego/edit/master/subnets/config.md --- # Subnet Configs It is possible to provide parameters for a Subnet. Parameters here apply to all chains in the specified Subnet. AvalancheGo looks for files specified with `{subnetID}.json` under `--subnet-config-dir` as documented [here](https://build.avax.network/docs/nodes/configure/configs-flags#subnet-configs). Here is an example of Subnet config file: ```json { "validatorOnly": false, "consensusParameters": { "k": 25, "alpha": 18 } } ``` ## Parameters ### Private Subnet #### `validatorOnly` (bool) If `true` this node does not expose Subnet blockchain contents to non-validators via P2P messages. Defaults to `false`. Avalanche Subnets are public by default. It means that every node can sync and listen ongoing transactions/blocks in Subnets, even they're not validating the listened Subnet. Subnet validators can choose not to publish contents of blockchains via this configuration. If a node sets `validatorOnly` to true, the node exchanges messages only with this Subnet's validators. Other peers will not be able to learn contents of this Subnet from this node. :::tip This is a node-specific configuration. Every validator of this Subnet has to use this configuration in order to create a full private Subnet. ::: #### `allowedNodes` (string list) If `validatorOnly=true` this allows explicitly specified NodeIDs to be allowed to sync the Subnet regardless of validator status. Defaults to be empty. :::tip This is a node-specific configuration. Every validator of this Subnet has to use this configuration in order to properly allow a node in the private Subnet. ::: ### Consensus Parameters Subnet configs supports loading new consensus parameters. JSON keys are different from their matching `CLI` keys. These parameters must be grouped under `consensusParameters` key. The consensus parameters of a Subnet default to the same values used for the Primary Network, which are given [CLI Snow Parameters](https://build.avax.network/docs/nodes/configure/configs-flags#snow-parameters). | CLI Key | JSON Key | | :------------------------------- | :-------------------- | | --snow-sample-size | k | | --snow-quorum-size | alpha | | --snow-commit-threshold | `beta` | | --snow-concurrent-repolls | concurrentRepolls | | --snow-optimal-processing | `optimalProcessing` | | --snow-max-processing | maxOutstandingItems | | --snow-max-time-processing | maxItemProcessingTime | | --snow-avalanche-batch-size | `batchSize` | | --snow-avalanche-num-parents | `parentSize` | #### `proposerMinBlockDelay` (duration) The minimum delay performed when building snowman++ blocks. Default is set to 1 second. As one of the ways to control network congestion, Snowman++ will only build a block `proposerMinBlockDelay` after the parent block's timestamp. Some high-performance custom VMs may find this too strict. This flag allows tuning the frequency at which blocks are built. ### Gossip Configs It's possible to define different Gossip configurations for each Subnet without changing values for Primary Network. JSON keys of these parameters are different from their matching `CLI` keys. These parameters default to the same values used for the Primary Network. For more information see [CLI Gossip Configs](https://build.avax.network/docs/nodes/configure/configs-flags#gossiping). | CLI Key | JSON Key | | :------------------------------------------------------ | :------------------------------------- | | --consensus-accepted-frontier-gossip-validator-size | gossipAcceptedFrontierValidatorSize | | --consensus-accepted-frontier-gossip-non-validator-size | gossipAcceptedFrontierNonValidatorSize | | --consensus-accepted-frontier-gossip-peer-size | gossipAcceptedFrontierPeerSize | | --consensus-on-accept-gossip-validator-size | gossipOnAcceptValidatorSize | | --consensus-on-accept-gossip-non-validator-size | gossipOnAcceptNonValidatorSize | | --consensus-on-accept-gossip-peer-size | gossipOnAcceptPeerSize | # Subnet-EVM Configs (/docs/nodes/chain-configs/avalanche-l1s/subnet-evm) --- title: "Subnet-EVM Configs" description: "This page describes the configuration options available for the Subnet-EVM." edit_url: https://github.com/ava-labs/subnet-evm/edit/master/plugin/evm/config/config.md --- # Subnet-EVM Configuration > **Note**: These are the configuration options available in the Subnet-EVM codebase. To set these values, you need to create a configuration file at `~/.avalanchego/configs/chains//config.json`. > > For the AvalancheGo node configuration options, see the AvalancheGo Configuration page. This document describes all configuration options available for Subnet-EVM. ## Example Configuration ```json { "eth-apis": ["eth", "eth-filter", "net", "web3"], "pruning-enabled": true, "commit-interval": 4096, "trie-clean-cache": 512, "trie-dirty-cache": 512, "snapshot-cache": 256, "rpc-gas-cap": 50000000, "log-level": "info", "metrics-expensive-enabled": true, "continuous-profiler-dir": "./profiles", "state-sync-enabled": false, "accepted-cache-size": 32 } ``` ## Configuration Format Configuration is provided as a JSON object. All fields are optional unless otherwise specified. ## API Configuration ### Ethereum APIs | Option | Type | Description | Default | |--------|------|-------------|---------| | `eth-apis` | array of strings | List of Ethereum services that should be enabled | `["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction"]` | ### Subnet-EVM Specific APIs | Option | Type | Description | Default | |--------|------|-------------|---------| | `validators-api-enabled` | bool | Enable the validators API | `true` | | `admin-api-enabled` | bool | Enable the admin API for administrative operations | `false` | | `admin-api-dir` | string | Directory for admin API operations | - | | `warp-api-enabled` | bool | Enable the Warp API for cross-chain messaging | `false` | ### API Limits and Security | Option | Type | Description | Default | |--------|------|-------------|---------| | `rpc-gas-cap` | uint64 | Maximum gas limit for RPC calls | `50,000,000` | | `rpc-tx-fee-cap` | float64 | Maximum transaction fee cap in AVAX | `100` | | `api-max-duration` | duration | Maximum duration for API calls (0 = no limit) | `0` | | `api-max-blocks-per-request` | int64 | Maximum number of blocks per getLogs request (0 = no limit) | `0` | | `http-body-limit` | uint64 | Maximum size of HTTP request bodies | - | | `batch-request-limit` | uint64 | Maximum number of requests that can be batched in an RPC call. For no limit, set either this or `batch-response-max-size` to 0 | `1000` | | `batch-response-max-size` | uint64 | Maximum size (in bytes) of response that can be returned from a batched RPC call. For no limit, set either this or `batch-request-limit` to 0. Defaults to `25 MB`| `1000` | ### WebSocket Settings | Option | Type | Description | Default | |--------|------|-------------|---------| | `ws-cpu-refill-rate` | duration | Rate at which WebSocket CPU usage quota is refilled (0 = no limit) | `0` | | `ws-cpu-max-stored` | duration | Maximum stored WebSocket CPU usage quota (0 = no limit) | `0` | ## Cache Configuration ### Trie Caches | Option | Type | Description | Default | |--------|------|-------------|---------| | `trie-clean-cache` | int | Size of the trie clean cache in MB | `512` | | `trie-dirty-cache` | int | Size of the trie dirty cache in MB | `512` | | `trie-dirty-commit-target` | int | Memory limit to target in the dirty cache before performing a commit in MB | `20` | | `trie-prefetcher-parallelism` | int | Maximum concurrent disk reads trie prefetcher should perform | `16` | ### Other Caches | Option | Type | Description | Default | |--------|------|-------------|---------| | `snapshot-cache` | int | Size of the snapshot disk layer clean cache in MB | `256` | | `accepted-cache-size` | int | Depth to keep in the accepted headers and logs cache (blocks) | `32` | | `state-sync-server-trie-cache` | int | Trie cache size for state sync server in MB | `64` | ## Ethereum Settings ### Transaction Processing | Option | Type | Description | Default | |--------|------|-------------|---------| | `preimages-enabled` | bool | Enable preimage recording | `false` | | `allow-unfinalized-queries` | bool | Allow queries for unfinalized blocks | `false` | | `allow-unprotected-txs` | bool | Allow unprotected transactions (without EIP-155) | `false` | | `allow-unprotected-tx-hashes` | array | List of specific transaction hashes allowed to be unprotected | EIP-1820 registry tx | | `local-txs-enabled` | bool | Enable treatment of transactions from local accounts as local | `false` | ### Snapshots | Option | Type | Description | Default | |--------|------|-------------|---------| | `snapshot-wait` | bool | Wait for snapshot generation on startup | `false` | | `snapshot-verification-enabled` | bool | Enable snapshot verification | `false` | ## Pruning and State Management ### Basic Pruning | Option | Type | Description | Default | |--------|------|-------------|---------| | `pruning-enabled` | bool | Enable state pruning to save disk space | `true` | | `commit-interval` | uint64 | Interval at which to persist EVM and atomic tries (blocks) | `4096` | | `accepted-queue-limit` | int | Maximum blocks to queue before blocking during acceptance | `64` | ### State Reconstruction | Option | Type | Description | Default | |--------|------|-------------|---------| | `allow-missing-tries` | bool | Suppress warnings about incomplete trie index | `false` | | `populate-missing-tries` | uint64 | Starting block for re-populating missing tries (null = disabled) | `null` | | `populate-missing-tries-parallelism` | int | Concurrent readers for re-populating missing tries | `1024` | ### Offline Pruning | Option | Type | Description | Default | |--------|------|-------------|---------| | `offline-pruning-enabled` | bool | Enable offline pruning | `false` | | `offline-pruning-bloom-filter-size` | uint64 | Bloom filter size for offline pruning in MB | `512` | | `offline-pruning-data-directory` | string | Directory for offline pruning data | - | ### Historical Data | Option | Type | Description | Default | |--------|------|-------------|---------| | `historical-proof-query-window` | uint64 | Number of blocks before last accepted for proof queries (archive mode only, ~24 hours) | `43200` | | `state-history` | uint64 | Number of most recent states that are accesible on disk (pruning mode only) | `32` | ## Transaction Pool Configuration | Option | Type | Description | Default | |--------|------|-------------|---------| | `tx-pool-price-limit` | uint64 | Minimum gas price for transaction acceptance | - | | `tx-pool-price-bump` | uint64 | Minimum price bump percentage for transaction replacement | - | | `tx-pool-account-slots` | uint64 | Maximum number of executable transaction slots per account | - | | `tx-pool-global-slots` | uint64 | Maximum number of executable transaction slots for all accounts | - | | `tx-pool-account-queue` | uint64 | Maximum number of non-executable transaction slots per account | - | | `tx-pool-global-queue` | uint64 | Maximum number of non-executable transaction slots for all accounts | - | | `tx-pool-lifetime` | duration | Maximum time transactions can stay in the pool | - | ## Gossip Configuration ### Push Gossip Settings | Option | Type | Description | Default | |--------|------|-------------|---------| | `push-gossip-percent-stake` | float64 | Percentage of total stake to push gossip to (range: [0, 1]) | `0.9` | | `push-gossip-num-validators` | int | Number of validators to push gossip to | `100` | | `push-gossip-num-peers` | int | Number of non-validator peers to push gossip to | `0` | ### Regossip Settings | Option | Type | Description | Default | |--------|------|-------------|---------| | `push-regossip-num-validators` | int | Number of validators to regossip to | `10` | | `push-regossip-num-peers` | int | Number of non-validator peers to regossip to | `0` | | `priority-regossip-addresses` | array | Addresses to prioritize for regossip | - | ### Timing Configuration | Option | Type | Description | Default | |--------|------|-------------|---------| | `push-gossip-frequency` | duration | Frequency of push gossip | `100ms` | | `pull-gossip-frequency` | duration | Frequency of pull gossip | `1s` | | `regossip-frequency` | duration | Frequency of regossip | `30s` | ## Logging and Monitoring ### Logging | Option | Type | Description | Default | |--------|------|-------------|---------| | `log-level` | string | Logging level (trace, debug, info, warn, error, crit) | `"info"` | | `log-json-format` | bool | Use JSON format for logs | `false` | ### Profiling | Option | Type | Description | Default | |--------|------|-------------|---------| | `continuous-profiler-dir` | string | Directory for continuous profiler output (empty = disabled) | - | | `continuous-profiler-frequency` | duration | Frequency to run continuous profiler | `15m` | | `continuous-profiler-max-files` | int | Maximum number of profiler files to maintain | `5` | ### Metrics | Option | Type | Description | Default | |--------|------|-------------|---------| | `metrics-expensive-enabled` | bool | Enable expensive debug-level metrics; this includes Firewood metrics | `true` | ## Security and Access ### Keystore | Option | Type | Description | Default | |--------|------|-------------|---------| | `keystore-directory` | string | Directory for keystore files (absolute or relative path) | - | | `keystore-external-signer` | string | External signer configuration | - | | `keystore-insecure-unlock-allowed` | bool | Allow insecure account unlocking | `false` | ### Fee Configuration | Option | Type | Description | Default | |--------|------|-------------|---------| | `feeRecipient` | string | Address to send transaction fees to (leave empty if not supported) | - | ## Network and Sync ### Network | Option | Type | Description | Default | |--------|------|-------------|---------| | `max-outbound-active-requests` | int64 | Maximum number of outbound active requests for VM2VM network | `16` | ### State Sync | Option | Type | Description | Default | |--------|------|-------------|---------| | `state-sync-enabled` | bool | Enable state sync | `false` | | `state-sync-skip-resume` | bool | Force state sync to use highest available summary block | `false` | | `state-sync-ids` | string | Comma-separated list of state sync IDs | - | | `state-sync-commit-interval` | uint64 | Commit interval for state sync (blocks) | `16384` | | `state-sync-min-blocks` | uint64 | Minimum blocks ahead required for state sync | `300000` | | `state-sync-request-size` | uint16 | Number of key/values to request per state sync request | `1024` | ## Database Configuration > **WARNING**: `firewood` and `path` schemes are untested in production. Using `path` is strongly discouraged. To use `firewood`, you must also set the following config options: > > - `pruning-enabled: true` (enabled by default) > - `state-sync-enabled: false` > - `snapshot-cache: 0` Failing to set these options will result in errors on VM initialization. Additionally, not all APIs are available - see these portions of the config documentation for more details. | Option | Type | Description | Default | |--------|------|-------------|---------| | `database-type` | string | Type of database to use | `"pebbledb"` | | `database-path` | string | Path to database directory | - | | `database-read-only` | bool | Open database in read-only mode | `false` | | `database-config` | string | Inline database configuration | - | | `database-config-file` | string | Path to database configuration file | - | | `use-standalone-database` | bool | Use standalone database instead of shared one | - | | `inspect-database` | bool | Inspect database on startup | `false` | | `state-scheme` | string | EXPERIMENTAL: specifies the database scheme to store state data; can be one of `hash` or `firewood` | `hash` | ## Transaction Indexing | Option | Type | Description | Default | |--------|------|-------------|---------| | `transaction-history` | uint64 | Maximum number of blocks from head whose transaction indices are reserved (0 = no limit) | - | | `tx-lookup-limit` | uint64 | **Deprecated** - use `transaction-history` instead | - | | `skip-tx-indexing` | bool | Skip indexing transactions entirely | `false` | ## Warp Configuration | Option | Type | Description | Default | |--------|------|-------------|---------| | `warp-off-chain-messages` | array | Off-chain messages the node should be willing to sign | - | | `prune-warp-db-enabled` | bool | Clear warp database on startup | `false` | ## Miscellaneous | Option | Type | Description | Default | |--------|------|-------------|---------| | `airdrop` | string | Path to airdrop file | - | | `skip-upgrade-check` | bool | Skip checking that upgrades occur before last accepted block ⚠️ **Warning**: Only use when you understand the implications | `false` | | `min-delay-target` | integer | The minimum delay between blocks (in milliseconds) that this node will attempt to use when creating blocks | Parent block's target | ## Gossip Constants The following constants are defined for transaction gossip behavior and cannot be configured without a custom build of Subnet-EVM: | Constant | Type | Description | Value | |----------|------|-------------|-------| | Bloom Filter Min Target Elements | int | Minimum target elements for bloom filter | `8,192` | | Bloom Filter Target False Positive Rate | float | Target false positive rate | `1%` | | Bloom Filter Reset False Positive Rate | float | Reset false positive rate | `5%` | | Bloom Filter Churn Multiplier | int | Churn multiplier | `3` | | Push Gossip Discarded Elements | int | Number of discarded elements | `16,384` | | Tx Gossip Target Message Size | size | Target message size for transaction gossip | `20 KiB` | | Tx Gossip Throttling Period | duration | Throttling period | `10s` | | Tx Gossip Throttling Limit | int | Throttling limit | `2` | | Tx Gossip Poll Size | int | Poll size | `1` | ## Validation Notes - Cannot enable `populate-missing-tries` while pruning or offline pruning is enabled - Cannot run offline pruning while pruning is disabled - Commit interval must be non-zero when pruning is enabled - `push-gossip-percent-stake` must be in range `[0, 1]` - Some settings may require node restart to take effect # Amazon Web Services (/docs/nodes/run-a-node/on-third-party-services/amazon-web-services) --- title: Amazon Web Services description: Learn how to run a node on Amazon Web Services. --- Introduction[​](#introduction "Direct link to heading") ------------------------------------------------------- This tutorial will guide you through setting up an Avalanche node on [Amazon Web Services (AWS)](https://aws.amazon.com/). Cloud services like AWS are a good way to ensure that your node is highly secure, available, and accessible. To get started, you'll need: - An AWS account - A terminal with which to SSH into your AWS machine - A place to securely store and back up files This tutorial assumes your local machine has a Unix style terminal. If you're on Windows, you'll have to adapt some of the commands used here. Log Into AWS[​](#log-into-aws "Direct link to heading") ------------------------------------------------------- Signing up for AWS is outside the scope of this article, but Amazon has instructions [here](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account). It is _highly_ recommended that you set up Multi-Factor Authentication on your AWS root user account to protect it. Amazon has documentation for this [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html#enable-virt-mfa-for-root). Once your account is set up, you should create a new EC2 instance. An EC2 is a virtual machine instance in AWS's cloud. Go to the [AWS Management Console](https://console.aws.amazon.com/) and enter the EC2 dashboard. ![AWS Management Console.png](/images/amazon1.png) To log into the EC2 instance, you will need a key on your local machine that grants access to the instance. First, create that key so that it can be assigned to the EC2 instance later on. On the bar on the left side, under **Network & Security**, select **Key Pairs.** ![Select "Key Pairs" under the "Network & Security" drop-down.](/images/amazon2.png) Select **Create key pair** to launch the key pair creation wizard. ![Select "Create key pair."](/images/amazon3.png) Name your key `avalanche`. If your local machine has MacOS or Linux, select the `pem` file format. If it's Windows, use the `ppk` file format. Optionally, you can add tags for the key pair to assist with tracking. ![Create a key pair that will later be assigned to your EC2 instance.](/images/amazon4.png) Click `Create key pair`. You should see a success message, and the key file should be downloaded to your local machine. Without this file, you will not be able to access your EC2 instance. **Make a copy of this file and put it on a separate storage medium such as an external hard drive. Keep this file secret; do not share it with others.** ![Success message after creating a key pair.](/images/amazon5.png) Create a Security Group[​](#create-a-security-group "Direct link to heading") ----------------------------------------------------------------------------- An AWS Security Group defines what internet traffic can enter and leave your EC2 instance. Think of it like a firewall. Create a new Security Group by selecting **Security Groups** under the **Network & Security** drop-down. ![Select "Security Groups" underneath "Network & Security."](/images/amazon6.png) This opens the Security Groups panel. Click **Create security group** in the top right of the Security Groups panel. ![Select "Create security group."](/images/amazon7.png) You'll need to specify what inbound traffic is allowed. Allow SSH traffic from your IP address so that you can log into your EC2 instance (each time your ISP changes your IP address, you will need to modify this rule). Allow TCP traffic on port 9651 so your node can communicate with other nodes on the network. Allow TCP traffic on port 9650 from your IP so you can make API calls to your node. **It's important that you only allow traffic on the SSH and API port from your IP.** If you allow incoming traffic from anywhere, this could be used to brute force entry to your node (SSH port) or used as a denial of service attack vector (API port). Finally, allow all outbound traffic. ![Your inbound and outbound rules should look like this.](/images/amazon8.png) Add a tag to the new security group with key `Name` and value`Avalanche Security Group`. This will enable us to know what this security group is when we see it in the list of security groups. ![Tag the security group so you can identify it later.](/images/amazon9.png) Click `Create security group`. You should see the new security group in the list of security groups. Launch an EC2 Instance[​](#launch-an-ec2-instance "Direct link to heading") --------------------------------------------------------------------------- Now you're ready to launch an EC2 instance. Go to the EC2 Dashboard and select **Launch instance**. ![Select "Launch Instance."](/images/amazon10.png) Select **Ubuntu 20.04 LTS (HVM), SSD Volume Type** for the operating system. ![Select Ubuntu 20.04 LTS.](/images/amazon11.png) Next, choose your instance type. This defines the hardware specifications of the cloud instance. In this tutorial we set up a **c5.2xlarge**. This should be more than powerful enough since Avalanche is a lightweight consensus protocol. To create a c5.2xlarge instance, select the **Compute-optimized** option from the filter drop-down menu. ![Filter by compute optimized.](/images/amazon12.png) Select the checkbox next to the c5.2xlarge instance in the table. ![Select c5.2xlarge.](/images/amazon13.png) Click the **Next: Configure Instance Details** button in the bottom right-hand corner. ![Configure instance details](/images/amazon14.png) The instance details can stay as their defaults. When setting up a node as a validator, it is crucial to select the appropriate AWS instance type to ensure the node can efficiently process transactions and manage the network load. The recommended instance types are as follows: - For a minimal stake, start with a compute-optimized instance such as c6, c6i, c6a, c7 and similar. - Use a 2xlarge instance size for the minimal stake configuration. - As the staked amount increases, choose larger instance sizes to accommodate the additional workload. For every order of magnitude increase in stake, move up one instance size. For example, for a 20k AVAX stake, a 4xlarge instance is suitable. ### Optional: Using Reserved Instances[​](#optional-using-reserved-instances "Direct link to heading") By default, you will be charged hourly for running your EC2 instance. For a long term usage that is not optimal. You could save money by using a **Reserved Instance**. With a reserved instance, you pay upfront for an entire year of EC2 usage, and receive a lower per-hour rate in exchange for locking in. If you intend to run a node for a long time and don't want to risk service interruptions, this is a good option to save money. Again, do your own research before selecting this option. ### Add Storage, Tags, Security Group[​](#add-storage-tags-security-group "Direct link to heading") Click the **Next: Add Storage** button in the bottom right corner of the screen. You need to add space to your instance's disk. You should start with at least 700GB of disk space. Although upgrades to reduce disk usage are always in development, on average the database will continually grow, so you need to constantly monitor disk usage on the node and increase disk space if needed. Note that the image below shows 100GB as disk size, which was appropriate at the time the screenshot was taken. You should check the current [recommended disk space size](https://github.com/ava-labs/avalanchego#installation) before entering the actual value here. ![Select disk size.](/images/amazon15.png) Click **Next: Add Tags** in the bottom right corner of the screen to add tags to the instance. Tags enable us to associate metadata with our instance. Add a tag with key `Name` and value `My Avalanche Node`. This will make it clear what this instance is on your list of EC2 instances. ![Add a tag with key "Name" and value "My Avalanche Node."](/images/amazon16.png) Now assign the security group created earlier to the instance. Choose **Select an existing security group** and choose the security group created earlier. ![Choose the security group created earlier.](/images/amazon17.png) Finally, click **Review and Launch** in the bottom right. A review page will show the details of the instance you're about to launch. Review those, and if all looks good, click the blue **Launch** button in the bottom right corner of the screen. You'll be asked to select a key pair for this instance. Select **Choose an existing key pair** and then select the `avalanche` key pair you made earlier in the tutorial. Check the box acknowledging that you have access to the `.pem` or `.ppk` file created earlier (make sure you've backed it up!) and then click **Launch Instances**. ![Use the key pair created earlier.](/images/amazon18.png) You should see a new pop up that confirms the instance is launching! ![Your instance is launching!](/images/amazon19.png) ### Assign an Elastic IP[​](#assign-an-elastic-ip "Direct link to heading") By default, your instance will not have a fixed IP. Let's give it a fixed IP through AWS's Elastic IP service. Go back to the EC2 dashboard. Under **Network & Security,** select **Elastic IPs**. ![Select "Elastic IPs" under "Network & Security."](/images/amazon20.png) Select **Allocate Elastic IP address**. ![Select "Allocate Elastic IP address."](/images/amazon21.png) Select the region your instance is running in, and choose to use Amazon's pool of IPv4 addresses. Click **Allocate**. ![Settings for the Elastic IP.](/images/amazon22.png) Select the Elastic IP you just created from the Elastic IP manager. From the **Actions** drop-down, choose **Associate Elastic IP address**. ![Under "Actions" select "Associate Elastic IP address."](/images/amazon23.png) Select the instance you just created. This will associate the new Elastic IP with the instance and give it a public IP address that won't change. ![Assign the Elastic IP to your EC2 instance.](/images/amazon24.png) Set Up AvalancheGo[​](#set-up-avalanchego "Direct link to heading") ------------------------------------------------------------------- Go back to the EC2 Dashboard and select `Running Instances`. ![Go to your running instances.](/images/amazon25.png) Select the newly created EC2 instance. This opens a details panel with information about the instance. ![Details about your new instance.](/images/amazon26.png) Copy the `IPv4 Public IP` field to use later. From now on we call this value `PUBLICIP`. **Remember: the terminal commands below assume you're running Linux. Commands may differ for MacOS or other operating systems. When copy-pasting a command from a code block, copy and paste the entirety of the text in the block.** Log into the AWS instance from your local machine. Open a terminal (try shortcut `CTRL + ALT + T`) and navigate to the directory containing the `.pem` file you downloaded earlier. Move the `.pem` file to `$HOME/.ssh` (where `.pem` files generally live) with: Add it to the SSH agent so that we can use it to SSH into your EC2 instance, and mark it as read-only. ```bash ssh-add ~/.ssh/avalanche.pem; chmod 400 ~/.ssh/avalanche.pem ``` SSH into the instance. (Remember to replace `PUBLICIP` with the public IP field from earlier.) If the permissions are **not** set correctly, you will see the following error. ![Make sure you set the permissions correctly.](/images/amazon27.png) You are now logged into the EC2 instance. ![You're on the EC2 instance.](/images/amazon28.png) If you have not already done so, update the instance to make sure it has the latest operating system and security updates: ```bash sudo apt update; sudo apt upgrade -y; sudo reboot ``` This also reboots the instance. Wait 5 minutes, then log in again by running this command on your local machine: You're logged into the EC2 instance again. Now we'll need to set up our Avalanche node. To do this, follow the [Set Up Avalanche Node With Installer](/docs/nodes/run-a-node/using-install-script/installing-avalanche-go) tutorial which automates the installation process. You will need the `PUBLICIP` we set up earlier. Your AvalancheGo node should now be running and in the process of bootstrapping, which can take a few hours. To check if it's done, you can issue an API call using `curl`. If you're making the request from the EC2 instance, the request is: ```bash curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.isBootstrapped", "params": { "chain":"X" } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` Once the node is finished bootstrapping, the response will be: ```json { "jsonrpc": "2.0", "result": { "isBootstrapped": true }, "id": 1 } ``` You can continue on, even if AvalancheGo isn't done bootstrapping. In order to make your node a validator, you'll need its node ID. To get it, run: ```bash curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.getNodeID" }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` The response contains the node ID. ```json {"jsonrpc":"2.0","result":{"nodeID":"NodeID-DznHmm3o7RkmpLkWMn9NqafH66mqunXbM"},"id":1} ``` In the above example the node ID is`NodeID-DznHmm3o7RkmpLkWMn9NqafH66mqunXbM`. Copy your node ID for later. Your node ID is not a secret, so you can just paste it into a text editor. AvalancheGo has other APIs, such as the [Health API](/docs/rpcs/other/health-rpc), that may be used to interact with the node. Some APIs are disabled by default. To enable such APIs, modify the ExecStart section of `/etc/systemd/system/avalanchego.service` (created during the installation process) to include flags that enable these endpoints. Don't manually enable any APIs unless you have a reason to. ![Some APIs are disabled by default.](/images/amazon29.png) Back up the node's staking key and certificate in case the EC2 instance is corrupted or otherwise unavailable. The node's ID is derived from its staking key and certificate. If you lose your staking key or certificate then your node will get a new node ID, which could cause you to become ineligible for a staking reward if your node is a validator. **It is very strongly advised that you copy your node's staking key and certificate**. The first time you run a node, it will generate a new staking key/certificate pair and store them in directory `/home/ubuntu/.avalanchego/staking`. Exit out of the SSH instance by running: Now you're no longer connected to the EC2 instance; you're back on your local machine. To copy the staking key and certificate to your machine, run the following command. As always, replace `PUBLICIP`. ```bash scp -r ubuntu@PUBLICIP:/home/ubuntu/.avalanchego/staking ~/aws_avalanche_backup ``` Now your staking key and certificate are in directory `~/aws_avalanche_backup` . **The contents of this directory are secret.** You should hold this directory on storage not connected to the internet (like an external hard drive.) ### Upgrading Your Node[​](#upgrading-your-node "Direct link to heading") AvalancheGo is an ongoing project and there are regular version upgrades. Most upgrades are recommended but not required. Advance notice will be given for upgrades that are not backwards compatible. To update your node to the latest version, SSH into your AWS instance as before and run the installer script again. ```bash ./avalanchego-installer.sh ``` Your machine is now running the newest AvalancheGo version. To see the status of the AvalancheGo service, run `sudo systemctl status avalanchego.` Increase Volume Size[​](#increase-volume-size "Direct link to heading") ----------------------------------------------------------------------- If you need to increase the volume size, follow these instructions from AWS: - [Request modifications to your EBS volumes](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/requesting-ebs-volume-modifications.html) - [Extend a Linux file system after resizing a volume](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html) Wrap Up[​](#wrap-up "Direct link to heading") --------------------------------------------- That's it! You now have an AvalancheGo node running on an AWS EC2 instance. We recommend setting up [node monitoring](/docs/nodes/maintain/monitoring)for your AvalancheGo node. We also recommend setting up AWS billing alerts so you're not surprised when the bill arrives. If you have feedback on this tutorial, or anything else, send us a message on [Discord](https://chat.avalabs.org/). # AWS Marketplace (/docs/nodes/run-a-node/on-third-party-services/aws-marketplace) --- title: AWS Marketplace description: Learn how to run a node on AWS Marketplace. --- ## How to Launch an Avalanche Validator using AWS With the intention of enabling developers and entrepreneurs to on-ramp into the Avalanche ecosystem with as little friction as possible, Ava Labs recently launched an offering to deploy an Avalanche Validator node via the AWS Marketplace. This tutorial will show the main steps required to get this node running and validating on the Avalanche Fuji testnet. Product Overview[​](#product-overview "Direct link to heading") --------------------------------------------------------------- The Avalanche Validator node is available via [the AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-nd6wgi2bhhslg). There you'll find a high level product overview. This includes a product description, pricing information, usage instructions, support information and customer reviews. After reviewing this information you want to click the "Continue to Subscribe" button. Subscribe to This Software[​](#subscribe-to-this-software "Direct link to heading") ----------------------------------------------------------------------------------- Once on the "Subscribe to this Software" page you will see a button which enables you to subscribe to this AWS Marketplace offering. In addition you'll see Terms of service including the seller's End User License Agreement and the [AWS Privacy Notice](https://aws.amazon.com/privacy/). After reviewing these you want to click on the "Continue to Configuration" button. Configure This Software[​](#configure-this-software "Direct link to heading") ----------------------------------------------------------------------------- This page lets you choose a fulfillment option and software version to launch this software. No changes are needed as the default settings are sufficient. Leave the `Fulfillment Option` as `64-bit (x86) Amazon Machine Image (AMI)`. The software version is the latest build of [the AvalancheGo full node](https://github.com/ava-labs/avalanchego/releases), `v1.9.5 (Dec 22, 2022)`, AKA `Banff.5`. This will always show the latest version. Also, the Region to deploy in can be left as `US East (N. Virginia)`. On the right you'll see the software and infrastructure pricing. Lastly, click the "Continue to Launch" button. Launch This Software[​](#launch-this-software "Direct link to heading") ----------------------------------------------------------------------- Here you can review the launch configuration details and follow the instructions to launch the Avalanche Validator Node. The changes are very minor. Leave the action as "Launch from Website." The EC2 Instance Type should remain `c5.2xlarge`. The primary change you'll need to make is to choose a keypair which will enable you to `ssh` into the newly created EC2 instance to run `curl` commands on the Validator node. You can search for existing Keypairs or you can create a new keypair and download it to your local machine. If you create a new keypair you'll need to move the keypair to the appropriate location, change the permissions and add it to the OpenSSH authentication agent. For example, on MacOS it would look similar to the following: ```bash # In this example we have a keypair called avalanche.pem which was downloaded from AWS to ~/Downloads/avalanche.pem # Confirm the file exists with the following command test -f ~/Downloads/avalanche.pem && echo "Avalanche.pem exists." # Running the above command will output the following: # Avalanche.pem exists. # Move the avalanche.pem keypair from the ~/Downloads directory to the hidden ~/.ssh directory mv ~/Downloads/avalanche.pem ~/.ssh # Next add the private key identity to the OpenSSH authentication agent ssh-add ~/.ssh/avalanche.pem; # Change file modes or Access Control Lists sudo chmod 600 ~/.ssh/avalanche.pem ``` Once these steps are complete you are ready to launch the Validator node on EC2. To make that happen click the "Launch" button ![launch successful](/images/aws1.png) You now have an Avalanche node deployed on an AWS EC2 instance! Copy the `AMI ID` and click on the `EC2 Console` link for the next step. EC2 Console[​](#ec2-console "Direct link to heading") ----------------------------------------------------- Now take the `AMI ID` from the previous step and input it into the search bar on the EC2 Console. This will bring you to the dashboard where you can find the EC2 instances public IP address. ![AMI instance](/images/aws2.png) Copy that public IP address and open a Terminal or command line prompt. Once you have the new Terminal open `ssh` into the EC2 instance with the following command. Node Configuration[​](#node-configuration "Direct link to heading") ------------------------------------------------------------------- ### Switch to Fuji Testnet[​](#switch-to-fuji-testnet "Direct link to heading") By default the Avalanche Node available through the AWS Marketplace syncs the Mainnet. If this is what you are looking for, you can skip this step. For this tutorial you want to sync and validate the Fuji Testnet. Now that you're `ssh`ed into the EC2 instance you can make the required changes to sync Fuji instead of Mainnet. First, confirm that the node is syncing the Mainnet by running the `info.getNetworkID` command. #### `info.getNetworkID` Request[​](#infogetnetworkid-request "Direct link to heading") ```bash curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.getNetworkID", "params": { } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` #### `info.getNetworkID` Response[​](#infogetnetworkid-response "Direct link to heading") The returned `networkID` will be 1 which is the network ID for Mainnet. ```json { "jsonrpc": "2.0", "result": { "networkID": "1" }, "id": 1 } ``` Now you want to edit `/etc/avalanchego/conf.json` and change the `"network-id"` property from `"mainnet"` to `"fuji"`. To see the contents of `/etc/avalanchego/conf.json` you can `cat` the file. ```bash cat /etc/avalanchego/conf.json { "api-keystore-enabled": false, "http-host": "0.0.0.0", "log-dir": "/var/log/avalanchego", "db-dir": "/data/avalanchego", "api-admin-enabled": false, "public-ip-resolution-service": "opendns", "network-id": "mainnet" } ``` Edit that `/etc/avalanchego/conf.json` with your favorite text editor and change the value of the `"network-id"` property from `"mainnet"` to `"fuji"`. Once that's complete, save the file and restart the Avalanche node via `sudo systemctl restart avalanchego`. You can then call the `info.getNetworkID` endpoint to confirm the change was successful. #### `info.getNetworkID` Request[​](#infogetnetworkid-request-1 "Direct link to heading") ```bash curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.getNetworkID", "params": { } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` #### `info.getNetworkID` Response[​](#infogetnetworkid-response-1 "Direct link to heading") The returned `networkID` will be 5 which is the network ID for Fuji. ```json { "jsonrpc": "2.0", "result": { "networkID": "5" }, "id": 1 } ``` Next you run the `info.isBoostrapped` command to confirm if the Avalanche Validator node has finished bootstrapping. ### `info.isBootstrapped` Request[​](#infoisbootstrapped-request "Direct link to heading") ```bash curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.isBootstrapped", "params": { "chain":"P" } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` Once the node is finished bootstrapping, the response will be: ### `info.isBootstrapped` Response[​](#infoisbootstrapped-response "Direct link to heading") ```json { "jsonrpc": "2.0", "result": { "isBootstrapped": true }, "id": 1 } ``` **Note** that initially the response is `false` because the network is still syncing. When you're adding your node as a Validator on the Avalanche Mainnet you'll want to wait for this response to return `true` so that you don't suffer from any downtime while validating. For this tutorial you're not going to wait for it to finish syncing as it's not strictly necessary. ### `info.getNodeID` Request[​](#infogetnodeid-request "Direct link to heading") Next, you want to get the NodeID which will be used to add the node as a Validator. To get the node's ID you call the `info.getNodeID` jsonrpc endpoint. ```bash curl --location --request POST 'http://127.0.0.1:9650/ext/info' \ --header 'Content-Type: application/json' \ --data-raw '{ "jsonrpc":"2.0", "id" :1, "method" :"info.getNodeID", "params" :{ } }' ``` ### `info.getNodeID` Response[​](#infogetnodeid-response "Direct link to heading") Take a note of the `nodeID` value which is returned as you'll need to use it in the next step when adding a validator via the Avalanche Web Wallet. In this case the `nodeID` is `NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5` ```json { "jsonrpc": "2.0", "result": { "nodeID": "NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5", "nodePOP": { "publicKey": "0x85675db18b326a9585bfd43892b25b71bf01b18587dc5fac136dc5343a9e8892cd6c49b0615ce928d53ff5dc7fd8945d", "proofOfPossession": "0x98a56f092830161243c1f1a613ad68a7f1fb25d2462ecf85065f22eaebb4e93a60e9e29649a32252392365d8f628b2571174f520331ee0063a94473f8db6888fc3a722be330d5c51e67d0d1075549cb55376e1f21d1b48f859ef807b978f65d9" } }, "id": 1 } ``` Add Node as Validator on Fuji via Core web[​](#add-node-as-validator-on-fuji-via-core-web "Direct link to heading") ------------------------------------------------------------------------------------------------------------------- For adding the new node as a Validator on the Fuji testnet's Primary Network you can use the [Core web](https://core.app/) [connected](https://support.avax.network/en/articles/6639869-core-web-how-do-i-connect-to-core-web) to [Core extension](https://core.app). If you don't have a Core extension already, check out this [guide](https://support.avax.network/en/articles/6100129-core-extension-how-do-i-create-a-new-wallet). If you'd like to import an existing wallet to Core extension, follow [these steps](https://support.avax.network/en/articles/6078933-core-extension-how-do-i-access-my-existing-account). ![Core web](/images/aws3.png) Core web is a free, all-in-one command center that gives users a more intuitive and comprehensive way to view assets, and use dApps across the Avalanche network, its various Avalanche L1s, and Ethereum. Core web is optimized for use with the Core browser extension and Core mobile (available on both iOS & Android). Together, they are key components of the Core product suite that brings dApps, NFTs, Avalanche Bridge, Avalanche L1s, L2s, and more, directly to users. ### Switching to Testnet Mode[​](#switching-to-testnet-mode "Direct link to heading") By default, Core web and Core extension are connected to Mainnet. For the sake of this demo, you want to connect to the Fuji Testnet. #### On Core Extension[​](#on-core-extension "Direct link to heading") From the hamburger menu on the top-left corner, choose Advanced, and then toggle the Testnet Mode on. ![](/images/aws4.gif) You can follow the same steps for switching back to Mainnet. #### On Core web[​](#on-core-web "Direct link to heading") Click on the Settings button top-right corner of the page, then toggle Testnet Mode on. ![](/images/aws5.gif) You can follow the same steps for switching back to Mainnet. ### Adding the Validator[​](#adding-the-validator "Direct link to heading") - Node ID: A unique ID derived from each individual node's staker certificate. Use the `NodeID` which was returned in the `info.getNodeID` response. In this example it's `NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5` - Staking End Date: Your AVAX tokens will be locked until this date. - Stake Amount: The amount of AVAX to lock for staking. On Mainnet, the minimum required amount is 2,000 AVAX. On Testnet the minimum required amount is 1 AVAAX. - Delegation Fee: You will claim this % of the rewards from the delegators on your node. - Reward Address: A reward address is the destination address of the accumulated staking rewards. To add a node as a Validator, first select the Stake tab on Core web, in the left hand nav menu. Next click the Validate button, and select Get Started. ![](/images/aws6.gif) This page will open up. ![](/images/aws7.png) Choose the desired Staking Amount, then click Next. ![](/images/aws8.png) Enter you Node ID, then click Next. ![](/images/aws9.png) Here, you'll need to choose the staking duration. There are predefined values, like 1 day, 1 month and so on. You can also choose a custom period of time. For this example, 22 days were chosen. ![](/images/aws10.png) Choose the address that the network will send rewards to. Make sure it's the correct address because once the transaction is submitted this cannot be changed later or undone. You can choose the wallet's P-Chain address, or a custom P-Chain address. After entering the address, click Next. ![](/images/aws11.png) Other individuals can stake to your validator and receive rewards too, known as "delegating." You will claim this percent of the rewards from the delegators on your node. Click Next. ![](/images/aws12.png) After entering all these details, a summary of your validation will show up. If everything is correct, you can proceed and click on Submit Validation. A new page will open up, prompting you to accept the transaction. Here, please approve the transaction. ![](/images/aws13.png) After the transaction is approved, you will see a message saying that your validation transaction was submitted. ![](/images/aws14.png) If you click on View on explorer, a new browser tab will open with the details of the `AddValidatorTx`. It will show details such as the total value of AVAX transferred, any AVAX which were burned, the blockchainID, the blockID, the NodeID of the validator, and the total time which has elapsed from the entire Validation period. ![](/images/aws15.png) Confirm That the Node is a Pending Validator on Fuji[​](#confirm-that-the-node-is-a-pending-validator-on-fuji "Direct link to heading") --------------------------------------------------------------------------------------------------------------------------------------- As a last step you can call the `platform.getPendingvalidators` endpoint to confirm that the Avalanche node which was recently spun up on AWS is no in the pending validators queue where it will stay for 5 minutes. ### `platform.getPendingValidators` Request[​](#platformgetpendingvalidators-request "Direct link to heading") ```bash curl --location --request POST 'https://api.avax-test.network/ext/bc/P' \ --header 'Content-Type: application/json' \ --data-raw '{ "jsonrpc": "2.0", "method": "platform.getPendingValidators", "params": { "subnetID": "11111111111111111111111111111111LpoYY", "nodeIDs": [] }, "id": 1 }' ``` ### `platform.getPendingValidators` Response[​](#platformgetpendingvalidators-response "Direct link to heading") ```json { "jsonrpc": "2.0", "result": { "validators": [ { "txID": "4d7ZboCrND4FjnyNaF3qyosuGQsNeJ2R4KPJhHJ55VCU1Myjd", "startTime": "1673411918", "endTime": "1675313170", "stakeAmount": "1000000000", "nodeID": "NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5", "delegationFee": "2.0000", "connected": false, "delegators": null } ], "delegators": [] }, "id": 1 } ``` You can also pass in the `NodeID` as a string to the `nodeIDs` array in the request body. ```bash curl --location --request POST 'https://api.avax-test.network/ext/bc/P' \ --header 'Content-Type: application/json' \ --data-raw '{ "jsonrpc": "2.0", "method": "platform.getPendingValidators", "params": { "subnetID": "11111111111111111111111111111111LpoYY", "nodeIDs": ["NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5"] }, "id": 1 }' ``` This will filter the response by the `nodeIDs` array which will save you time by no longer requiring you to search through the entire response body for the NodeIDs. ```json { "jsonrpc": "2.0", "result": { "validators": [ { "txID": "4d7ZboCrND4FjnyNaF3qyosuGQsNeJ2R4KPJhHJ55VCU1Myjd", "startTime": "1673411918", "endTime": "1675313170", "stakeAmount": "1000000000", "nodeID": "NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5", "delegationFee": "2.0000", "connected": false, "delegators": null } ], "delegators": [] }, "id": 1 } ``` After 5 minutes the node will officially start validating the Avalanche Fuji testnet and you will no longer see it in the response body for the `platform.getPendingValidators` endpoint. Now you will access it via the `platform.getCurrentValidators` endpoint. ### `platform.getCurrentValidators` Request[​](#platformgetcurrentvalidators-request "Direct link to heading") ```bash curl --location --request POST 'https://api.avax-test.network/ext/bc/P' \ --header 'Content-Type: application/json' \ --data-raw '{ "jsonrpc": "2.0", "method": "platform.getCurrentValidators", "params": { "subnetID": "11111111111111111111111111111111LpoYY", "nodeIDs": ["NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5"] }, "id": 1 }' ``` ### `platform.getCurrentValidators` Response[​](#platformgetcurrentvalidators-response "Direct link to heading") ```json { "jsonrpc": "2.0", "result": { "validators": [ { "txID": "2hy57Z7KiZ8L3w2KonJJE1fs5j4JDzVHLjEALAHaXPr6VMeDhk", "startTime": "1673411918", "endTime": "1675313170", "stakeAmount": "1000000000", "nodeID": "NodeID-Q8Gfaaio9FAqCmZVEXDq9bFvNPvDi7rt5", "rewardOwner": { "locktime": "0", "threshold": "1", "addresses": [ "P-fuji1tgj2c3k56enytw5d78rt0tsq3lzg8wnftffwk7" ] }, "validationRewardOwner": { "locktime": "0", "threshold": "1", "addresses": [ "P-fuji1tgj2c3k56enytw5d78rt0tsq3lzg8wnftffwk7" ] }, "delegationRewardOwner": { "locktime": "0", "threshold": "1", "addresses": [ "P-fuji1tgj2c3k56enytw5d78rt0tsq3lzg8wnftffwk7" ] }, "potentialReward": "5400963", "delegationFee": "2.0000", "uptime": "0.0000", "connected": false, "delegators": null } ] }, "id": 1 } ``` Mainnet[​](#mainnet "Direct link to heading") --------------------------------------------- All of these steps can be applied to Mainnet. However, the minimum required Avax token amounts to become a validator is 2,000 on the Mainnet. For more information, please read [this doc](/docs/primary-network/validate/how-to-stake#validators). Maintenance[​](#maintenance "Direct link to heading") ----------------------------------------------------- AWS one click is meant to be used in automated environments, not as an end-user solution. You can still manage it manually, but it is not as easy as an Ubuntu instance or using the script: - AvalancheGo binary is at `/usr/local/bin/avalanchego` - Main node config is at `/etc/avalanchego/conf.json` - Working directory is at `/home/avalanche/.avalanchego/ (and belongs to avalanchego user)` - Database is at `/data/avalanchego` - Logs are at `/var/log/avalanchego` For a simple upgrade you would need to place the new binary at `/usr/local/bin/`. If you run an Avalanche L1, you would also need to place the VM binary into `/home/avalanche/.avalanchego/plugins`. You can also look at using [this guide](https://docs.aws.amazon.com/systems-manager/latest/userguide/automation-tutorial-update-ami.html), but that won't address updating the Avalanche L1, if you have one. Summary[​](#summary "Direct link to heading") --------------------------------------------- Avalanche is the first decentralized smart contracts platform built for the scale of global finance, with near-instant transaction finality. Now with an Avalanche Validator node available as a one-click install from the AWS Marketplace developers and entrepreneurs can on-ramp into the Avalanche ecosystem in a matter of minutes. If you have any questions or want to follow up in any way please join our Discord server at [https://discord.gg/avax](https://discord.gg/avax/). For more developer resources please check out our [Developer Documentation](/docs). # Google Cloud (/docs/nodes/run-a-node/on-third-party-services/google-cloud) --- title: Google Cloud description: Learn how to run an Avalanche node on Google Cloud. --- This document was written by a community member, some information may be outdated. Introduction[​](#introduction "Direct link to heading") ------------------------------------------------------- Google's Cloud Platform (GCP) is a scalable, trusted and reliable hosting platform. Google operates a significant amount of it's own global networking infrastructure. It's [fiber network](https://cloud.google.com/blog/products/networking/google-cloud-networking-in-depth-cloud-cdn) can provide highly stable and consistent global connectivity. In this article, we will leverage GCP to deploy a node on which Avalanche can installed via [terraform](https://www.terraform.io/). Leveraging `terraform` may seem like overkill, it should set you apart as an operator and administrator as it will enable you greater flexibility and provide the basis on which you can easily build further automation. Conventions[​](#conventions "Direct link to heading") ----------------------------------------------------- - `Items` highlighted in this manor are GCP parlance and can be searched for further reference in the Google documentation for their cloud products. Important Notes[​](#important-notes "Direct link to heading") ------------------------------------------------------------- - The machine type used in this documentation is for reference only and the actual sizing you use will depend entirely upon the amount that is staked and delegated to the node. Architectural Description[​](#architectural-description "Direct link to heading") --------------------------------------------------------------------------------- This section aims to describe the architecture of the system that the steps in the [Setup Instructions](#-setup-instructions) section deploy when enacted. This is done so that the executor can not only deploy the reference architecture, but also understand and potentially optimize it for their needs. ### Project[​](#project "Direct link to heading") We will create and utilize a single GCP `Project` for deployment of all resources. #### Service Enablement[​](#service-enablement "Direct link to heading") Within our GCP project we will need to enable the following Cloud Services: - `Compute Engine` - `IAP` ### Networking[​](#networking "Direct link to heading") #### Compute Network[​](#compute-network "Direct link to heading") We will deploy a single `Compute Network` object. This unit is where we will deploy all subsequent networking objects. It provides a logical boundary and securitization context should you wish to deploy other chain stacks or other infrastructure in GCP. #### Public IP[​](#public-ip "Direct link to heading") Avalanche requires that a validator communicate outbound on the same public IP address that it advertises for other peers to connect to it on. Within GCP this precludes the possibility of us using a Cloud NAT Router for the outbound communications and requires us to bind the public IP that we provision to the interface of the machine. We will provision a single `EXTERNAL` static IPv4 `Compute Address`. #### Avalanche L1s[​](#avalanche-l1s "Direct link to heading") For the purposes of this documentation we will deploy a single `Compute Subnetwork` in the US-EAST1 `Region` with a /24 address range giving us 254 IP addresses (not all usable but for the sake of generalized documentation). ### Compute[​](#compute "Direct link to heading") #### Disk[​](#disk "Direct link to heading") We will provision a single 400GB `PD-SSD` disk that will be attached to our VM. #### Instance[​](#instance "Direct link to heading") We will deploy a single `Compute Instance` of size `e2-standard-8`. Observations of operations using this machine specification suggest it is memory over provisioned and could be brought down to 16GB using custom machine specification; but please review and adjust as needed (the beauty of compute virtualization!!). #### Zone[​](#zone "Direct link to heading") We will deploy our instance into the `US-EAST1-B` `Zone` #### Firewall[​](#firewall "Direct link to heading") We will provision the following `Compute Firewall` rules: - IAP INGRESS for SSH (TCP 22) - this only allows GCP IAP sources inbound on SSH. - P2P INGRESS for AVAX Peers (TCP 9651) These are obviously just default ports and can be tailored to your needs as you desire. Setup Instructions[​](#-setup-instructions "Direct link to heading") -------------------------------------------------------------------- ### GCP Account[​](#gcp-account "Direct link to heading") 1. If you don't already have a GCP account go create one [here](https://console.cloud.google.com/freetrial) You will get some free bucks to run a trial, the trial is feature complete but your usage will start to deplete your free bucks so turn off anything you don't need and/or add a credit card to your account if you intend to run things long term to avoid service shutdowns. ### Project[​](#project-1 "Direct link to heading") Login to the GCP `Cloud Console` and create a new `Project` in your organization. Let's use the name `my-avax-nodes` for the sake of this setup. 1. ![Select Project Dropdown](/images/cloud1.png) 2. ![Click New Project Button](/images/cloud2.png) 3. ![Create New Project](/images/cloud3.png) ### Terraform State[​](#terraform-state "Direct link to heading") Terraform uses a state files to compose a differential between current infrastructure configuration and the proposed plan. You can store this state in a variety of different places, but using GCP storage is a reasonable approach given where we are deploying so we will stick with that. 1. ![Select Cloud Storage Browser](/images/cloud4.png) 2. ![Create New Bucket](/images/cloud5.png) Authentication to GCP from terraform has a few different options which are laid out [here](https://www.terraform.io/language/settings/backends/gcs). Please chose the option that aligns with your context and ensure those steps are completed before continuing. Depending upon how you intend to execute your terraform operations you may or may not need to enable public access to the bucket. Obviously, not exposing the bucket for `public` access (even if authenticated) is preferable. If you intend to simply run terraform commands from your local machine then you will need to open the access up. I recommend to employ a full CI/CD pipeline using GCP Cloud Build which if utilized will mean the bucket can be marked as `private`. A full walk through of Cloud Build setup in this context can be found [here](https://cloud.google.com/architecture/managing-infrastructure-as-code) ### Clone GitHub Repository[​](#clone-github-repository "Direct link to heading") I have provided a rudimentary terraform construct to provision a node on which to run Avalanche which can be found [here](https://github.com/meaghanfitzgerald/deprecated-avalanche-docs/tree/master/static/scripts). Documentation below assumes you are using this repository but if you have another terraform skeleton similar steps will apply. ### Terraform Configuration[​](#terraform-configuration "Direct link to heading") 1. If running terraform locally, please [install](https://learn.hashicorp.com/tutorials/terraform/install-cli) it. 2. In this repository, navigate to the `terraform` directory. 3. Under the `projects` directory, rename the `my-avax-project` directory to match your GCP project name that you created (not required, but nice to be consistent) 4. Under the folder you just renamed locate the `terraform.tfvars` file. 5. Edit this file and populate it with the values which make sense for your context and save it. 6. Locate the `backend.tf` file in the same directory. 7. Edit this file ensuring to replace the `bucket` property with the GCS bucket name that you created earlier. If you do not with to use cloud storage to persist terraform state then simply switch the `backend` to some other desirable provider. ### Terraform Execution[​](#terraform-execution "Direct link to heading") Terraform enables us to see what it would do if we were to run it without actually applying any changes... this is called a `plan` operation. This plan is then enacted (optionally) by an `apply`. #### Plan[​](#plan "Direct link to heading") 1. In a terminal which is able to execute the `tf` binary, `cd` to the ~`my-avax-project` directory that you renamed in step 3 of `Terraform Configuration`. 2. Execute the command `tf plan` 3. You should see a JSON output to the stdout of the terminal which lays out the operations that terraform will execute to apply the intended state. #### Apply[​](#apply "Direct link to heading") 1. In a terminal which is able to execute the `tf` binary, `cd` to the ~`my-avax-project` directory that you renamed in step 3 of `Terraform Configuration`. 2. Execute the command `tf apply` If you want to ensure that terraform does **exactly** what you saw in the `apply` output, you can optionally request for the `plan` output to be saved to a file to feed to `apply`. This is generally considered best practice in highly fluid environments where rapid change is occurring from multiple sources. Conclusion[​](#conclusion "Direct link to heading") --------------------------------------------------- Establishing CI/CD practices using tools such as GitHub and Terraform to manage your infrastructure assets is a great way to ensure base disaster recovery capabilities and to ensure you have a place to embed any ~tweaks you have to make operationally removing the potential to miss them when you have to scale from 1 node to 10. Having an automated pipeline also gives you a place to build a bigger house... what starts as your interest in building and managing a single AVAX node today can quickly change into you building an infrastructure operation for many different chains working with multiple different team members. I hope this may have inspired you to take a leap into automation in this context! # Latitude (/docs/nodes/run-a-node/on-third-party-services/latitude) --- title: Latitude description: Learn how to run an Avalanche node on Latitude.sh. --- Introduction[​](#introduction "Direct link to heading") ------------------------------------------------------- This tutorial will guide you through setting up an Avalanche node on [Latitude.sh](https://latitude.sh/). Latitude.sh provides high-performance lighting-fast bare metal servers to ensure that your node is highly secure, available, and accessible. To get started, you'll need: - A Latitude.sh account - A terminal with which to SSH into your Latitude.sh machine For the instructions on creating an account and server with Latitude.sh, please reference their [GitHub tutorial](https://github.com/NottherealIllest/Latitude.sh-post/blob/main/avalanhe/avax-copy.md) , or visit [this page](https://www.latitude.sh/dashboard/signup) to sign up and create your first project. This tutorial assumes your local machine has a Unix-style terminal. If you're on Windows, you'll have to adapt some of the commands used here. Configuring Your Server[​](#configuring-your-server "Direct link to heading") ----------------------------------------------------------------------------- ### Create a Latitude.sh Account[​](#create-a-latitudesh-account "Direct link to heading") At this point your account has been verified, and you have created a new project and deployed the server according to the instructions linked above. ### Access Your Server & Further Steps[​](#access-your-server--further-steps "Direct link to heading") All your Latitude.sh credentials are available by clicking the `server` under your project, and can be used to access your Latitude.sh machine from your local machine using a terminal. You will need to install the `avalanche node installer script` directly in the server's terminal. After gaining access, we'll need to set up our Avalanche node. To do this, follow the instructions here to install and run your node [Set Up Avalanche Node With Installer](/docs/nodes/run-a-node/using-install-script/installing-avalanche-go). Your AvalancheGo node should now be running and in the process of bootstrapping, which can take a few hours. To check if it's done, you can issue an API call using `curl`. The request is: ```bash curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.isBootstrapped", "params": { "chain":"X" } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` Once the node is finished bootstrapping, the response will be: ```json { "jsonrpc": "2.0", "result": { "isBootstrapped": true }, "id": 1 } ``` You can continue on, even if AvalancheGo isn't done bootstrapping. In order to make your node a validator, you'll need its node ID. To get it, run: ```bash curl -X POST --data '{ "jsonrpc": "2.0", "id": 1, "method": "info.getNodeID" }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` The response contains the node ID. ```json { "jsonrpc": "2.0", "result": { "nodeID": "KhDnAoZDW8iRJ3F26iQgK5xXVFMPcaYeu" }, "id": 1 } ``` In the above example the node ID is `NodeID-KhDnAoZDW8iRJ3F26iQgK5xXVFMPcaYeu`. AvalancheGo has other APIs, such as the [Health API](/docs/rpcs/other/health-rpc), that may be used to interact with the node. Some APIs are disabled by default. To enable such APIs, modify the ExecStart section of `/etc/systemd/system/avalanchego.service` (created during the installation process) to include flags that enable these endpoints. Don't manually enable any APIs unless you have a reason to. Exit out of the SSH server by running: ### Upgrading Your Node[​](#upgrading-your-node "Direct link to heading") AvalancheGo is an ongoing project and there are regular version upgrades. Most upgrades are recommended but not required. Advance notice will be given for upgrades that are not backwards compatible. To update your node to the latest version, SSH into your server using a terminal and run the installer script again. ```bash ./avalanchego-installer.sh ``` Your machine is now running the newest AvalancheGo version. To see the status of the AvalancheGo service, run `sudo systemctl status avalanchego.` Wrap Up[​](#wrap-up "Direct link to heading") --------------------------------------------- That's it! You now have an AvalancheGo node running on a Latitude.sh machine. We recommend setting up [node monitoring](/docs/nodes/maintain/monitoring) for your AvalancheGo node. # Microsoft Azure (/docs/nodes/run-a-node/on-third-party-services/microsoft-azure) --- title: Microsoft Azure description: How to run an Avalanche node on Microsoft Azure. --- This document was written by a community member, some information may be out of date. Running a validator and staking with Avalanche provides extremely competitive rewards of between 9.69% and 11.54% depending on the length you stake for. The maximum rate is earned by staking for a year, whilst the lowest rate for 14 days. There is also no slashing, so you don't need to worry about a hardware failure or bug in the client which causes you to lose part or all of your stake. Instead with Avalanche you only need to currently maintain at least 80% uptime to receive rewards. If you fail to meet this requirement you don't get slashed, but you don't receive the rewards. **You also do not need to put your private keys onto a node to begin validating on that node.** Even if someone breaks into your cloud environment and gains access to the node, the worst they can do is turn off the node. Not only does running a validator node enable you to receive rewards in AVAX, but later you will also be able to validate other Avalanche L1s in the ecosystem as well and receive rewards in the token native to their Avalanche L1s. Hardware requirements to run a validator are relatively modest: 8 CPU cores, 16 GB of RAM and 1 TB SSD. It also doesn't use enormous amounts of energy. Avalanche's [revolutionary consensus mechanism](/docs/primary-network/avalanche-consensus) is able to scale to millions of validators participating in consensus at once, offering unparalleled decentralisation. Currently the minimum amount required to stake to become a validator is 2,000 AVAX. Alternatively, validators can also charge a small fee to enable users to delegate their stake with them to help towards running costs. In this article we will step through the process of configuring a node on Microsoft Azure. This tutorial assumes no prior experience with Microsoft Azure and will go through each step with as few assumptions possible. At the time of this article, spot pricing for a virtual machine with 2 Cores and 8 GB memory costs as little as 0.01060perhourwhichworksoutatabout0.01060 per hour which works out at about 113.44 a year, **a saving of 83.76%! compared to normal pay as you go prices.** In comparison a virtual machine in AWS with 2 Cores and 4 GB Memory with spot pricing is around $462 a year. Initial Subscription Configuration[​](#initial-subscription-configuration "Direct link to heading") --------------------------------------------------------------------------------------------------- ### Set up 2 Factor[​](#set-up-2-factor "Direct link to heading") First you will need a Microsoft Account, if you don't have one already you will see an option to create one at the following link. If you already have one, make sure to set up 2 Factor authentication to secure your node by going to the following link and then selecting "Two-step verification" and following the steps provided. [https://account.microsoft.com/security](https://account.microsoft.com/security) ![Image for post](/images/azure1.png) Once two factor has been configured log into the Azure portal by going to [https://portal.azure.com](https://portal.azure.com/) and signing in with your Microsoft account. When you login you won't have a subscription, so we need to create one first. Select "Subscriptions" as highlighted below: ![Image for post](/images/azure2.png) Then select "+ Add" to add a new subscription ![Image for post](/images/azure3.png) If you want to use Spot Instance VM Pricing (which will be considerably cheaper) you can't use a Free Trial account (and you will receive an error upon validation), so **make sure to select Pay-As-You-Go.** ![Image for post](/images/azure4.png) Enter your billing details and confirm identity as part of the sign-up process, when you get to Add technical support select the without support option (unless you want to pay extra for support) and press Next. ![Image for post](/images/azure5.png) Create a Virtual Machine[​](#create-a-virtual-machine "Direct link to heading") ------------------------------------------------------------------------------- Now that we have a subscription, we can create the Ubuntu Virtual Machine for our Avalanche Node. Select the Icon in the top left for the Menu and choose "+ Create a resource" ![Image for post](/images/azure6.png) Select Ubuntu Server 18.04 LTS (this will normally be under the popular section or alternatively search for it in the marketplace) ![Image for post](/images/azure7.png) This will take you to the Create a virtual machine page as shown below: ![Image for post](/images/azure8.png) First, enter a virtual machine a name, this can be anything but in my example, I have called it Avalanche (This will also automatically change the resource group name to match) Then select a region from the drop-down list. Select one of the recommended ones in a region that you prefer as these tend to be the larger ones with most features enabled and cheaper prices. In this example I have selected North Europe. ![Image for post](/images/azure9.png) You have the option of using spot pricing to save significant amounts on running costs. Spot instances use a supply and demand market price structure. As demand for instances goes up, the price for the spot instance goes up. If there is insufficient capacity, then your VM will be turned off. The chances of this happening are incredibly low though, especially if you select the Capacity only option. Even in the unlikely event it does get turned off temporarily you only need to maintain at least 80% up time to receive the staking rewards and there is no slashing implemented in Avalanche. Select Yes for Azure Spot instance, select Eviction type to Capacity Only and **make sure to set the eviction policy to Stop / Deallocate. This is very important otherwise the VM will be deleted** ![Image for post](/images/azure10.png) Choose "Select size" to change the Virtual Machine size, and from the menu select D2s\_v4 under the D-Series v4 selection (This size has 2 Cores, 8 GB Memory and enables Premium SSDs). You can use F2s\_v2 instances instead, with are 2 Cores, 4 GB Memory and enables Premium SSDs) but the spot price actually works out cheaper for the larger VM currently with spot instance prices. You can use [this link](https://azure.microsoft.com/en-us/pricing/details/virtual-machines/linux/) to view the prices across the different regions. ![Image for post](/images/azure11.png) Once you have selected the size of the Virtual Machine, select "View pricing history and compare prices in nearby regions" to see how the spot price has changed over the last 3 months, and whether it's cheaper to use a nearby region which may have more spare capacity. ![Image for post](/images/azure12.png) At the time of this article, spot pricing for D2s\_v4 in North Europe costs 0.07975perhour,oraround0.07975 per hour, or around 698.61 a year. With spot pricing, the price falls to 0.01295perhour,whichworksoutatabout0.01295 per hour, which works out at about 113.44 a year, **a saving of 83.76%!** There are some regions which are even cheaper, East US for example is 0.01060perhouroraround0.01060 per hour or around 92.86 a year! ![Image for post](/images/azure13.png) Below you can see the price history of the VM over the last 3 months for North Europe and regions nearby. ![Image for post](/images/azure14.png) ### Cheaper Than Amazon AWS[​](#cheaper-than-amazon-aws "Direct link to heading") As a comparison a c5.large instance costs 0.085USDperhouronAWS.Thistotals 0.085 USD per hour on AWS. This totals ~745 USD per year. Spot instances can save 62%, bringing that total down to $462. The next step is to change the username for the VM, to align with other Avalanche tutorials change the username to Ubuntu. Otherwise you will need to change several commands later in this article and swap out Ubuntu with your new username. ![Image for post](/images/azure15.png) ### Disks[​](#disks "Direct link to heading") Select Next: Disks to then configure the disks for the instance. There are 2 choices for disks, either Premium SSD which offer greater performance with a 64 GB disk costs around 10amonth,orthereisthestandardSSDwhichofferslowerperformanceandisaround10 a month, or there is the standard SSD which offers lower performance and is around 5 a month. You also have to pay $0.002 per 10,000 transaction units (reads / writes and deletes) with the Standard SSD, whereas with Premium SSDs everything is included. Personally, I chose the Premium SSD for greater performance, but also because the disks are likely to be heavily used and so may even work out cheaper in the long run. Select Next: Networking to move onto the network configuration ![Image for post](/images/azure16.png) ### Network Config[​](#network-config "Direct link to heading") You want to use a Static IP so that the public IP assigned to the node doesn't change in the event it stops. Under Public IP select "Create new" ![Image for post](/images/azure17.png) Then select "Static" as the Assignment type ![Image for post](/images/azure19.png) Then we need to configure the network security group to control access inbound to the Avalanche node. Select "Advanced" as the NIC network security group type and select "Create new" ![Image for post](/images/azure20.png) For security purposes you want to restrict who is able to remotely connect to your node. To do this you will first want to find out what your existing public IP is. This can be done by going to google and searching for "what's my IP" ![Image for post](/images/azure21.png) It's likely that you have been assigned a dynamic public IP for your home, unless you have specifically requested it, and so your assigned public IP may change in the future. It's still recommended to restrict access to your current IP though, and then in the event your home IP changes and you are no longer able to remotely connect to the VM, you can just update the network security rules with your new public IP so you are able to connect again. NOTE: If you need to change the network security group rules after deployment if your home IP has changed, search for "avalanche-nsg" and you can modify the rule for SSH and Port 9650 with the new IP. **Port 9651 needs to remain open to everyone** though as that's how it communicates with other Avalanche nodes. ![Image for post](/images/azure22.png) Now that you have your public IP select the default allow ssh rule on the left under inbound rules to modify it. Change Source from "Any" to "IP Addresses" and then enter in your Public IP address that you found from google in the Source IP address field. Change the Priority towards the bottom to 100 and then press Save. ![Image for post](/images/azure23.png) Then select "+ Add an inbound rule" to add another rule for RPC access, this should also be restricted to only your IP. Change Source to "IP Addresses" and enter in your public IP returned from google into the Source IP field. This time change the "Destination port ranges" field to 9650 and select "TCP" as the protocol. Change the priority to 110 and give it a name of "Avalanche\_RPC" and press Add. ![Image for post](/images/azure24.png) Select "+ Add an inbound rule" to add a final rule for the Avalanche Protocol so that other nodes can communicate with your node. This rule needs to be open to everyone so keep "Source" set to "Any." Change the Destination port range to "9651" and change the protocol to "TCP." Enter a priority of 120 and a name of Avalanche\_Protocol and press Add. ![Image for post](/images/azure25.png) The network security group should look like the below (albeit your public IP address will be different) and press OK. ![Image for post](/images/azure26.png) Leave the other settings as default and then press "Review + create" to create the Virtual machine. ![Image for post](/images/azure27.png) First it will perform a validation test. If you receive an error here, make sure you selected Pay-As-You-Go subscription model and you are not using the Free Trial subscription as Spot instances are not available. Verify everything looks correct and press "Create" ![Image for post](/images/azure28.png) You should then receive a prompt asking you to generate a new key pair to connect your virtual machine. Select "Download private key and create resource" to download the private key to your PC. ![Image for post](/images/azure29.png) Once your deployment has finished, select "Go to resource" ![Image for post](/images/azure30.png) Change the Provisioned Disk Size[​](#change-the-provisioned-disk-size "Direct link to heading") ----------------------------------------------------------------------------------------------- By default, the Ubuntu VM will be provisioned with a 30 GB Premium SSD. You should increase this to 250 GB, to allow for database growth. ![Image for post](/images/azure31.png) To change the Disk size, the VM needs to be stopped and deallocated. Select "Stop" and wait for the status to show deallocated. Then select "Disks" on the left. ![Image for post](/images/azure32.png) Select the Disk name that's current provisioned to modify it ![Image for post](/images/azure33.png) Select "Size + performance" on the left under settings and change the size to 250 GB and press "Resize" ![Image for post](/images/azure34.png) Doing this now will also extend the partition automatically within Ubuntu. To go back to the virtual machine overview page, select Avalanche in the navigation setting. ![Image for post](/images/azure35.png) Then start the VM ![Image for post](/images/azure36.png) Connect to the Avalanche Node[​](#connect-to-the-avalanche-node "Direct link to heading") ----------------------------------------------------------------------------------------- The following instructions show how to connect to the Virtual Machine from a Windows 10 machine. For instructions on how to connect from a Ubuntu machine see the [AWS tutorial](/docs/nodes/run-a-node/on-third-party-services/amazon-web-services). On your local PC, create a folder on the root of the C: drive called Avalanche and then move the Avalanche\_key.pem file you downloaded before into the folder. Then right click the file and select Properties. Go to the security tab and select "Advanced" at the bottom ![Image for post](/images/azure37.png) Select "Disable inheritance" and then "Remove all inherited permissions from this object" to remove all existing permissions on that file. ![Image for post](/images/azure38.png) Then select "Add" to add a new permission and choose "Select a principal" at the top. From the pop-up box enter in your user account that you use to log into your machine. In this example I log on with a local user called Seq, you may have a Microsoft account that you use to log in, so use whatever account you login to your PC with and press "Check Names" and it should underline it to verify and press OK. ![Image for post](/images/azure39.png) Then from the permissions section make sure only "Read & Execute" and "Read" are selected and press OK. ![Image for post](/images/azure40.png) It should look something like the below, except with a different PC name / user account. This just means the key file can't be modified or accessed by any other accounts on this machine for security purposes so they can't access your Avalanche Node. ![Image for post](/images/azure41.png) ### Find your Avalanche Node Public IP[​](#find-your-avalanche-node-public-ip "Direct link to heading") From the Azure Portal make a note of your static public IP address that has been assigned to your node. ![Image for post](/images/azure42.png) To log onto the Avalanche node, open command prompt by searching for `cmd` and selecting "Command Prompt" on your Windows 10 machine. ![Image for post](/images/azure43.png) Then use the following command and replace the EnterYourAzureIPHere with the static IP address shown on the Azure portal. ssh -i C:\\Avalanche\\Avalanche\_key.pem ubuntu@EnterYourAzureIPHere for my example its: ssh -i C:\\Avalanche\\Avalanche\_key.pem The first time you connect you will receive a prompt asking to continue, enter yes. ![Image for post](/images/azure44.png) You should now be connected to your Node. ![Image for post](/images/azure45.png) The following section is taken from Colin's excellent tutorial for [configuring an Avalanche Node on Amazon's AWS](/docs/nodes/run-a-node/on-third-party-services/amazon-web-services). ### Update Linux with Security Patches[​](#update-linux-with-security-patches "Direct link to heading") Now that we are on our node, it's a good idea to update it to the latest packages. To do this, run the following commands, one-at-a-time, in order: ``` sudo apt update sudo apt upgrade -y sudo reboot ``` ![Image for post](/images/azure46.png) This will make our instance up to date with the latest security patches for our operating system. This will also reboot the node. We'll give the node a minute or two to boot back up, then log in again, same as before. ### Set up the Avalanche Node[​](#set-up-the-avalanche-node "Direct link to heading") Now we'll need to set up our Avalanche node. To do this, follow the [Set Up Avalanche Node With Installer](/docs/nodes/run-a-node/using-install-script/installing-avalanche-go) tutorial which automates the installation process. You will need the "IPv4 Public IP" copied from the Azure Portal we set up earlier. Once the installation is complete, our node should now be bootstrapping! We can run the following command to take a peek at the latest status of the AvalancheGo node: ``` sudo systemctl status avalanchego ``` To check the status of the bootstrap, we'll need to make a request to the local RPC using `curl`. This request is as follows: ``` curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.isBootstrapped", "params": { "chain":"X" } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` The node can take some time (upward of an hour at this moment writing) to bootstrap. Bootstrapping means that the node downloads and verifies the history of the chains. Give this some time. Once the node is finished bootstrapping, the response will be: ``` { "jsonrpc": "2.0", "result": { "isBootstrapped": true }, "id": 1 } ``` We can always use `sudo systemctl status avalanchego` to peek at the latest status of our service as before, as well. ### Get Your NodeID[​](#get-your-nodeid "Direct link to heading") We absolutely must get our NodeID if we plan to do any validating on this node. This is retrieved from the RPC as well. We call the following curl command to get our NodeID. ``` curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.getNodeID" }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` If all is well, the response should look something like: ``` {"jsonrpc":"2.0","result":{"nodeID":"NodeID-Lve2PzuCvXZrqn8Stqwy9vWZux6VyGUCR"},"id":1} ``` That portion that says, "NodeID-Lve2PzuCvXZrqn8Stqwy9vWZux6VyGUCR" is our NodeID, the entire thing. Copy that and keep that in our notes. There's nothing confidential or secure about this value, but it's an absolute must for when we submit this node to be a validator. ### Backup Your Staking Keys[​](#backup-your-staking-keys "Direct link to heading") The last thing that should be done is backing up our staking keys in the untimely event that our instance is corrupted or terminated. It's just good practice for us to keep these keys. To back them up, we use the following command: ``` scp -i C:\Avalanche\avalanche_key.pem -r ubuntu@EnterYourAzureIPHere:/home/ubuntu/.avalanchego/staking C:\Avalanche ``` As before, we'll need to replace "EnterYourAzureIPHere" with the appropriate value that we retrieved. This backs up our staking key and staking certificate into the C:\\Avalanche folder we created before. ![Image for post](/images/azure47.png) # Installing AvalancheGo (/docs/nodes/run-a-node/using-install-script/installing-avalanche-go) --- title: Installing AvalancheGo description: Learn how to install AvalancheGo on your system. --- ## Running the Script So, now that you prepared your system and have the info ready, let's get to it. To download and run the script, enter the following in the terminal: ```bash wget -nd -m https://raw.githubusercontent.com/ava-labs/avalanche-docs/master/scripts/avalanchego-installer.sh;\ chmod 755 avalanchego-installer.sh;\ ./avalanchego-installer.sh ``` And we're off! The output should look something like this: ```bash AvalancheGo installer --------------------- Preparing environment... Found arm64 architecture... Looking for the latest arm64 build... Will attempt to download: https://github.com/ava-labs/avalanchego/releases/download/v1.1.1/avalanchego-linux-arm64-v1.1.1.tar.gz avalanchego-linux-arm64-v1.1.1.tar.gz 100%[=========================================================================>] 29.83M 75.8MB/s in 0.4s 2020-12-28 14:57:47 URL:https://github-production-release-asset-2e65be.s3.amazonaws.com/246387644/f4d27b00-4161-11eb-8fb2-156a992fd2c8?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20201228%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20201228T145747Z&X-Amz-Expires=300&X-Amz-Signature=ea838877f39ae940a37a076137c4c2689494c7e683cb95a5a4714c062e6ba018&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=246387644&response-content-disposition=attachment%3B%20filename%3Davalanchego-linux-arm64-v1.1.1.tar.gz&response-content-type=application%2Foctet-stream [31283052/31283052] -> "avalanchego-linux-arm64-v1.1.1.tar.gz" [1] Unpacking node files... avalanchego-v1.1.1/plugins/ avalanchego-v1.1.1/plugins/evm avalanchego-v1.1.1/avalanchego Node files unpacked into /home/ubuntu/avalanche-node ``` And then the script will prompt you for information about the network environment: ```bash To complete the setup some networking information is needed. Where is the node installed: 1) residential network (dynamic IP) 2) cloud provider (static IP) Enter your connection type [1,2]: ``` Enter `1` if you have dynamic IP, and `2` if you have a static IP. If you are on a static IP, it will try to auto-detect the IP and ask for confirmation. ```bash Detected '3.15.152.14' as your public IP. Is this correct? [y,n]: ``` Confirm with `y`, or `n` if the detected IP is wrong (or empty), and then enter the correct IP at the next prompt. Next, you have to set up RPC port access for your node. Those are used to query the node for its internal state, to send commands to the node, or to interact with the platform and its chains (sending transactions, for example). You will be prompted: ```bash RPC port should be public (this is a public API node) or private (this is a validator)? [public, private]: ``` - `private`: this setting only allows RPC requests from the node machine itself. - `public`: this setting exposes the RPC port to all network interfaces. As this is a sensitive setting you will be asked to confirm if choosing `public`. Please read the following note carefully: If you choose to allow RPC requests on any network interface you will need to set up a firewall to only let through RPC requests from known IP addresses, otherwise your node will be accessible to anyone and might be overwhelmed by RPC calls from malicious actors! If you do not plan to use your node to send RPC calls remotely, enter `private`. The script will then prompt you to choose whether to enable state sync setting or not: ```bash Do you want state sync bootstrapping to be turned on or off? [on, off]: ``` Turning state sync on will greatly increase the speed of bootstrapping, but will sync only the current network state. If you intend to use your node for accessing historical data (archival node) you should select `off`. Otherwise, select `on`. Validators can be bootstrapped with state sync turned on. The script will then continue with system service creation and finish with starting the service. ```bash Created symlink /etc/systemd/system/multi-user.target.wants/avalanchego.service → /etc/systemd/system/avalanchego.service. Done! Your node should now be bootstrapping. Node configuration file is /home/ubuntu/.avalanchego/configs/node.json C-Chain configuration file is /home/ubuntu/.avalanchego/configs/chains/C/config.json Plugin directory, for storing subnet VM binaries, is /home/ubuntu/.avalanchego/plugins To check that the service is running use the following command (q to exit): sudo systemctl status avalanchego To follow the log use (ctrl-c to stop): sudo journalctl -u avalanchego -f Reach us over on https://discord.gg/avax if you're having problems. ``` The script is finished, and you should see the system prompt again. ## Post Installation AvalancheGo should be running in the background as a service. You can check that it's running with: ```bash sudo systemctl status avalanchego ``` Below is an example of what the node's latest logs should look like: ```bash ● avalanchego.service - AvalancheGo systemd service Loaded: loaded (/etc/systemd/system/avalanchego.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2021-01-05 10:38:21 UTC; 51s ago Main PID: 2142 (avalanchego) Tasks: 8 (limit: 4495) Memory: 223.0M CGroup: /system.slice/avalanchego.service └─2142 /home/ubuntu/avalanche-node/avalanchego --public-ip-resolution-service=opendns --http-host= Jan 05 10:38:45 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:38:45]

avalanchego/vms/platformvm/vm.go#322: initializing last accepted block as 2FUFPVPxbTpKNn39moGSzsmGroYES4NZRdw3mJgNvMkMiMHJ9e Jan 05 10:38:45 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:38:45]

avalanchego/snow/engine/snowman/transitive.go#58: initializing consensus engine Jan 05 10:38:45 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:38:45] avalanchego/api/server.go#143: adding route /ext/bc/11111111111111111111111111111111LpoYY Jan 05 10:38:45 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:38:45] avalanchego/api/server.go#88: HTTP API server listening on ":9650" Jan 05 10:38:58 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:38:58]

avalanchego/snow/engine/common/bootstrapper.go#185: Bootstrapping started syncing with 1 vertices in the accepted frontier Jan 05 10:39:02 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:39:02]

avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go#210: fetched 2500 blocks Jan 05 10:39:04 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:39:04]

avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go#210: fetched 5000 blocks Jan 05 10:39:06 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:39:06]

avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go#210: fetched 7500 blocks Jan 05 10:39:09 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:39:09]

avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go#210: fetched 10000 blocks Jan 05 10:39:11 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:39:11]

avalanchego/snow/engine/snowman/bootstrap/bootstrapper.go#210: fetched 12500 blocks ``` Note the `active (running)` which indicates the service is running OK. You may need to press `q` to return to the command prompt. To find out your NodeID, which is used to identify your node to the network, run the following command: ```bash sudo journalctl -u avalanchego | grep "NodeID" ``` It will produce output like: ```bash Jan 05 10:38:38 ip-172-31-30-64 avalanchego[2142]: INFO [01-05|10:38:38] avalanchego/node/node.go#428: Set node's ID to 6seStrauyCnVV7NEVwRbfaT9B6EnXEzfY ``` Prepend `NodeID-` to the value to get, for example, `NodeID-6seStrauyCnVV7NEVwRbfaT9B6EnXEzfY`. Store that; it will be needed for staking or looking up your node. Your node should be in the process of bootstrapping now. You can monitor the progress by issuing the following command: ```bash sudo journalctl -u avalanchego -f ``` Press `ctrl+C` when you wish to stop reading node output. # Managing AvalancheGo (/docs/nodes/run-a-node/using-install-script/managing-avalanche-go) --- title: Managing AvalancheGo description: Learn how to start, stop and upgrade your AvalancheGo node --- ## Stop Your Node To stop AvalancheGo, run: ```bash sudo systemctl stop avalanchego ``` ## Start Your Node To start your node again, run: ```bash sudo systemctl start avalanchego ``` ## Upgrade Your Node AvalancheGo is an ongoing project and there are regular version upgrades. Most upgrades are recommended but not required. Advance notice will be given for upgrades that are not backwards compatible. When a new version of the node is released, you will notice log lines like: ```bash Jan 08 10:26:45 ip-172-31-16-229 avalanchego[6335]: INFO [01-08|10:26:45] avalanchego/network/peer.go#526: beacon 9CkG9MBNavnw7EVSRsuFr7ws9gascDQy3 attempting to connect with newer version avalanche/1.1.1. You may want to update your client ``` It is recommended to always upgrade to the latest version, because new versions bring bug fixes, new features and upgrades. To upgrade your node, just run the installer script again: ```bash ./avalanchego-installer.sh ``` It will detect that you already have AvalancheGo installed: ```bash AvalancheGo installer --------------------- Preparing environment... Found 64bit Intel/AMD architecture... Found AvalancheGo systemd service already installed, switching to upgrade mode. Stopping service... ``` It will then upgrade your node to the latest version, and after it's done, start the node back up, and print out the information about the latest version: ```bash Node upgraded, starting service... New node version: avalanche/1.1.1 [network=mainnet, database=v1.0.0, commit=f76f1fd5f99736cf468413bbac158d6626f712d2] Done! ``` # Node Config and Maintenance (/docs/nodes/run-a-node/using-install-script/node-config-maintenance) --- title: Node Config and Maintenance description: Advanced options for configuring and maintaining your AvalancheGo node. --- ## Advanced Node Configuration Without any additional arguments, the script installs the node in a most common configuration. But the script also enables various advanced options to be configured, via the command line prompts. Following is a list of advanced options and their usage: - `admin` - [Admin API](/docs/rpcs/other/admin-rpc) will be enabled - `archival` - disables database pruning and preserves the complete transaction history - `state-sync` - if `on` state-sync for the C-Chain is used, if `off` it will use regular transaction replay to bootstrap; state-sync is much faster, but has no historical data - `db-dir` - use to provide the full path to the location where the database will be stored - `fuji` - node will connect to Fuji testnet instead of the Mainnet - `index` - [Index API](/docs/rpcs/other/index-rpc) will be enabled - `ip` - use `dynamic`, `static` arguments, of enter a desired IP directly to be used as the public IP node will advertise to the network - `rpc` - use `any` or `local` argument to select any or local network interface to be used to listen for RPC calls - `version` - install a specific node version, instead of the latest. See [here](#using-a-previous-version) for usage. Configuring the `index` and `archival` options on an existing node will require a fresh bootstrap to recreate the database. Complete script usage can be displayed by entering: ```bash ./avalanchego-installer.sh --help ``` ### Unattended Installation[​](#unattended-installation "Direct link to heading") If you want to use the script in an automated environment where you cannot enter the data at the prompts you must provide at least the `rpc` and `ip` options. For example: ```bash ./avalanchego-installer.sh --ip 1.2.3.4 --rpc local ``` ### Usage Examples[​](#usage-examples "Direct link to heading") - To run a Fuji node with indexing enabled and autodetected static IP: ```bash ./avalanchego-installer.sh --fuji --ip static --index ``` - To run an archival Mainnet node with dynamic IP and database located at `/home/node/db`: ```bash ./avalanchego-installer.sh --archival --ip dynamic --db-dir /home/node/db ``` - To use C-Chain state-sync to quickly bootstrap a Mainnet node, with dynamic IP and local RPC only: ```bash ./avalanchego-installer.sh --state-sync on --ip dynamic --rpc local ``` - To reinstall the node using node version 1.7.10 and use specific IP and local RPC only: ```bash ./avalanchego-installer.sh --reinstall --ip 1.2.3.4 --version v1.7.10 --rpc local ``` Node Configuration[​](#node-configuration "Direct link to heading") ------------------------------------------------------------------- The file that configures node operation is `~/.avalanchego/configs/node.json`. You can edit it to add or change configuration options. The documentation of configuration options can be found [here](/docs/nodes/configure/configs-flags). Configuration may look like this: ```json { "public-ip-resolution-service": "opendns", "http-host": "" } ``` Note that the configuration file needs to be a properly formatted `JSON` file, so switches should formatted differently than they would be for the command line. Therefore, don't enter options like `--public-ip-resolution-service=opendns` as shown in the example above. The script also creates an empty C-Chain config file, located at `~/.avalanchego/configs/chains/C/config.json`. By editing that file, you can configure the C-Chain, as described in detail [here](/docs/nodes/configure/configs-flags). Using a Previous Version[​](#using-a-previous-version "Direct link to heading") ------------------------------------------------------------------------------- The installer script can also be used to install a version of AvalancheGo other than the latest version. To see a list of available versions for installation, run: ```bash ./avalanchego-installer.sh --list ``` It will print out a list, something like: ```bash AvalancheGo installer --------------------- Available versions: v1.3.2 v1.3.1 v1.3.0 v1.2.4-arm-fix v1.2.4 v1.2.3-signed v1.2.3 v1.2.2 v1.2.1 v1.2.0 ``` To install a specific version, run the script with `--version` followed by the tag of the version. For example: ```bash ./avalanchego-installer.sh --version v1.3.1 ``` Note that not all AvalancheGo versions are compatible. You should generally run the latest version. Running a version other than latest may lead to your node not working properly and, for validators, not receiving a staking reward. Thanks to community member [Jean Zundel](https://github.com/jzu) for the inspiration and help implementing support for installing non-latest node versions. Reinstall and Script Update[​](#reinstall-and-script-update "Direct link to heading") ------------------------------------------------------------------------------------- The installer script gets updated from time to time, with new features and capabilities added. To take advantage of new features or to recover from modifications that made the node fail, you may want to reinstall the node. To do that, fetch the latest version of the script from the web with: ```bash wget -nd -m https://raw.githubusercontent.com/ava-labs/builders-hub/master/scripts/avalanchego-installer.sh ``` After the script has updated, run it again with the `--reinstall` config flag: ```bash ./avalanchego-installer.sh --reinstall ``` This will delete the existing service file, and run the installer from scratch, like it was started for the first time. Note that the database and NodeID will be left intact. Removing the Node Installation[​](#removing-the-node-installation "Direct link to heading") ------------------------------------------------------------------------------------------- If you want to remove the node installation from the machine, you can run the script with the `--remove` option, like this: ```bash ./avalanchego-installer.sh --remove ``` This will remove the service, service definition file and node binaries. It will not remove the working directory, node ID definition or the node database. To remove those as well, you can type: Please note that this is irreversible and the database and node ID will be deleted! What Next?[​](#what-next "Direct link to heading") -------------------------------------------------- That's it, you're running an AvalancheGo node! Congratulations! Let us know you did it on our [X](https://x.com/avax), [Telegram](https://t.me/avalancheavax) or [Reddit](https://www.reddit.com/r/Avax/)! If you're on a residential network (dynamic IP), don't forget to set up port forwarding. If you're on a cloud service provider, you're good to go. Now you can [interact with your node](/docs/rpcs/other/guides/issuing-api-calls), [stake your tokens](/docs/primary-network/validate/what-is-staking), or level up your installation by setting up [node monitoring](/docs/nodes/maintain/monitoring) to get a better insight into what your node is doing. Also, you might want to use our [Postman Collection](/docs/tooling/avalanche-postman) to more easily issue commands to your node. Finally, if you haven't already, it is a good idea to [back up](/docs/nodes/maintain/backup-restore) important files in case you ever need to restore your node to a different machine. If you have any questions, or need help, feel free to contact us on our [Discord](https://chat.avalabs.org/) server. # Preparing Your Environment (/docs/nodes/run-a-node/using-install-script/preparing-environment) --- title: Preparing Your Environment description: Learn how to prepare your environment before using install script. --- We have a shell (bash) script that installs AvalancheGo on your computer. This script sets up full, running node in a matter of minutes with minimal user input required. Script can also be used for unattended, automated installs. This install script assumes: - AvalancheGo is not running and not already installed as a service - User running the script has superuser privileges (can run `sudo`) Environment Considerations[​](#environment-considerations "Direct link to heading") ----------------------------------------------------------------------------------- If you run a different flavor of Linux, the script might not work as intended. It assumes `systemd` is used to run system services. Other Linux flavors might use something else, or might have files in different places than is assumed by the script. It will probably work on any distribution that uses `systemd` but it has been developed for and tested on Ubuntu. If you have a node already running on the computer, stop it before running the script. Script won't touch the node working directory so you won't need to bootstrap the node again. ### Node Running from Terminal[​](#node-running-from-terminal "Direct link to heading") If your node is running in a terminal stop it by pressing `ctrl+C`. ### Node Running as a Service[​](#node-running-as-a-service "Direct link to heading") If your node is already running as a service, then you probably don't need this script. You're good to go. ### Node Running in the Background[​](#node-running-in-the-background "Direct link to heading") If your node is running in the background (by running with `nohup`, for example) then find the process running the node by running `ps aux | grep avalanche`. This will produce output like: ```bash ubuntu 6834 0.0 0.0 2828 676 pts/1 S+ 19:54 0:00 grep avalanche ubuntu 2630 26.1 9.4 2459236 753316 ? Sl Dec02 1220:52 /home/ubuntu/build/avalanchego ``` Look for line that doesn't have `grep` on it. In this example, that is the second line. It shows information about your node. Note the process id, in this case, `2630`. Stop the node by running `kill -2 2630`. ### Node Working Files[​](#node-working-files "Direct link to heading") If you previously ran an AvalancheGo node on this computer, you will have local node files stored in `$HOME/.avalanchego` directory. Those files will not be disturbed, and node set up by the script will continue operation with the same identity and state it had before. That being said, for your node's security, back up `staker.crt` and `staker.key` files, found in `$HOME/.avalanchego/staking` and store them somewhere secure. You can use those files to recreate your node on a different computer if you ever need to. Check out this [tutorial](/docs/nodes/maintain/backup-restore) for backup and restore procedure. Networking Considerations[​](#networking-considerations "Direct link to heading") --------------------------------------------------------------------------------- To run successfully, AvalancheGo needs to accept connections from the Internet on the network port `9651`. Before you proceed with the installation, you need to determine the networking environment your node will run in. ### Running on a Cloud Provider[​](#running-on-a-cloud-provider "Direct link to heading") If your node is running on a cloud provider computer instance, it will have a static IP. Find out what that static IP is, or set it up if you didn't already. The script will try to find out the IP by itself, but that might not work in all environments, so you will need to check the IP or enter it yourself. ### Running on a Home Connection[​](#running-on-a-home-connection "Direct link to heading") If you're running a node on a computer that is on a residential internet connection, you have a dynamic IP; that is, your IP will change periodically. The install script will configure the node appropriately for that situation. But, for a home connection, you will need to set up inbound port forwarding of port `9651` from the internet to the computer the node is installed on. As there are too many models and router configurations, we cannot provide instructions on what exactly to do, but there are online guides to be found (like [this](https://www.noip.com/support/knowledgebase/general-port-forwarding-guide/), or [this](https://www.howtogeek.com/66214/how-to-forward-ports-on-your-router/) ), and your service provider support might help too. Please note that a fully connected Avalanche node maintains and communicates over a couple of thousand of live TCP connections. For some low-powered and older home routers that might be too much to handle. If that is the case you may experience lagging on other computers connected to the same router, node getting benched, failing to sync and similar issues. # Banff Changes (/docs/rpcs/other/guides/banff-changes) --- title: Banff Changes description: This document specifies the changes in Avalanche “Banff”, which was released in AvalancheGo v1.9.x. --- Block Changes[​](#block-changes "Direct link to heading") --------------------------------------------------------- ### Apricot[​](#apricot "Direct link to heading") Apricot allows the following block types with the following content: - _Standard Blocks_ may contain multiple transactions of the following types: - CreateChainTx - CreateSubnetTx - ImportTx - ExportTx - _Proposal Blocks_ may contain a single transaction of the following types: - AddValidatorTx - AddDelegatorTx - AddSubnetValidatorTx - RewardValidatorTx - AdvanceTimeTx - _Options Blocks_, that is _Commit Block_ and _Abort Block_ do not contain any transactions. Each block has a header containing: - ParentID - Height ### Banff[​](#banff "Direct link to heading") Banff allows the following block types with the following content: - _Standard Blocks_ may contain multiple transactions of the following types: - CreateChainTx - CreateSubnetTx - ImportTx - ExportTx - AddValidatorTx - AddDelegatorTx - AddSubnetValidatorTx - _RemoveSubnetValidatorTx_ - _TransformSubnetTx_ - _AddPermissionlessValidatorTx_ - _AddPermissionlessDelegatorTx_ - _Proposal Blocks_ may contain a single transaction of the following types: - RewardValidatorTx - _Options blocks_, that is _Commit Block_ and _Abort Block_ do not contain any transactions. Note that each block has an header containing: - ParentID - Height - _Time_ So the two main differences with respect to Apricot are: - _AddValidatorTx_, _AddDelegatorTx_, _AddSubnetValidatorTx_ are included into Standard Blocks rather than Proposal Blocks so that they don't need to be voted on (that is followed by a Commit/Abort Block). - New Transaction types: _RemoveSubnetValidatorTx_, _TransformSubnetTx_, _AddPermissionlessValidatorTx_, and _AddPermissionlessDelegatorTx_ have been added into Standard Blocks. - Block timestamp is explicitly serialized into block header, to allow chain time update. ### New Transactions[​](#new-transactions "Direct link to heading") #### RemoveSubnetValidatorTx[​](#removesubnetvalidatortx "Direct link to heading") ``` type RemoveSubnetValidatorTx struct { BaseTx `serialize:"true"` // The node to remove from the Avalanche L1. NodeID ids.NodeID `serialize:"true" json:"nodeID"` // The Avalanche L1 to remove the node from. Subnet ids.ID `serialize:"true" json:"subnet"` // Proves that the issuer has the right to remove the node from the Avalanche L1. SubnetAuth verify.Verifiable `serialize:"true" json:"subnetAuthorization"` } ``` #### TransformSubnetTx[​](#transformsubnettx "Direct link to heading") ``` type TransformSubnetTx struct { // Metadata, inputs and outputs BaseTx `serialize:"true"` // ID of the Subnet to transform // Restrictions: // - Must not be the Primary Network ID Subnet ids.ID `serialize:"true" json:"subnetID"` // Asset to use when staking on the Avalanche L1 // Restrictions: // - Must not be the Empty ID // - Must not be the AVAX ID AssetID ids.ID `serialize:"true" json:"assetID"` // Amount to initially specify as the current supply // Restrictions: // - Must be > 0 InitialSupply uint64 `serialize:"true" json:"initialSupply"` // Amount to specify as the maximum token supply // Restrictions: // - Must be >= [InitialSupply] MaximumSupply uint64 `serialize:"true" json:"maximumSupply"` // MinConsumptionRate is the rate to allocate funds if the validator's stake // duration is 0 MinConsumptionRate uint64 `serialize:"true" json:"minConsumptionRate"` // MaxConsumptionRate is the rate to allocate funds if the validator's stake // duration is equal to the minting period // Restrictions: // - Must be >= [MinConsumptionRate] // - Must be <= [reward.PercentDenominator] MaxConsumptionRate uint64 `serialize:"true" json:"maxConsumptionRate"` // MinValidatorStake is the minimum amount of funds required to become a // validator. // Restrictions: // - Must be > 0 // - Must be <= [InitialSupply] MinValidatorStake uint64 `serialize:"true" json:"minValidatorStake"` // MaxValidatorStake is the maximum amount of funds a single validator can // be allocated, including delegated funds. // Restrictions: // - Must be >= [MinValidatorStake] // - Must be <= [MaximumSupply] MaxValidatorStake uint64 `serialize:"true" json:"maxValidatorStake"` // MinStakeDuration is the minimum number of seconds a staker can stake for. // Restrictions: // - Must be > 0 MinStakeDuration uint32 `serialize:"true" json:"minStakeDuration"` // MaxStakeDuration is the maximum number of seconds a staker can stake for. // Restrictions: // - Must be >= [MinStakeDuration] // - Must be <= [GlobalMaxStakeDuration] MaxStakeDuration uint32 `serialize:"true" json:"maxStakeDuration"` // MinDelegationFee is the minimum percentage a validator must charge a // delegator for delegating. // Restrictions: // - Must be <= [reward.PercentDenominator] MinDelegationFee uint32 `serialize:"true" json:"minDelegationFee"` // MinDelegatorStake is the minimum amount of funds required to become a // delegator. // Restrictions: // - Must be > 0 MinDelegatorStake uint64 `serialize:"true" json:"minDelegatorStake"` // MaxValidatorWeightFactor is the factor which calculates the maximum // amount of delegation a validator can receive. // Note: a value of 1 effectively disables delegation. // Restrictions: // - Must be > 0 MaxValidatorWeightFactor byte `serialize:"true" json:"maxValidatorWeightFactor"` // UptimeRequirement is the minimum percentage a validator must be online // and responsive to receive a reward. // Restrictions: // - Must be <= [reward.PercentDenominator] UptimeRequirement uint32 `serialize:"true" json:"uptimeRequirement"` // Authorizes this transformation SubnetAuth verify.Verifiable `serialize:"true" json:"subnetAuthorization"` } ``` #### AddPermissionlessValidatorTx[​](#addpermissionlessvalidatortx "Direct link to heading") ``` type AddPermissionlessValidatorTx struct { // Metadata, inputs and outputs BaseTx `serialize:"true"` // Describes the validator Validator validator.Validator `serialize:"true" json:"validator"` // ID of the Avalanche L1 this validator is validating Subnet ids.ID `serialize:"true" json:"subnet"` // Where to send staked tokens when done validating StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"` // Where to send validation rewards when done validating ValidatorRewardsOwner fx.Owner `serialize:"true" json:"validationRewardsOwner"` // Where to send delegation rewards when done validating DelegatorRewardsOwner fx.Owner `serialize:"true" json:"delegationRewardsOwner"` // Fee this validator charges delegators as a percentage, times 10,000 // For example, if this validator has DelegationShares=300,000 then they // take 30% of rewards from delegators DelegationShares uint32 `serialize:"true" json:"shares"` } ``` #### AddPermissionlessDelegatorTx[​](#addpermissionlessdelegatortx "Direct link to heading") ``` type AddPermissionlessDelegatorTx struct { // Metadata, inputs and outputs BaseTx `serialize:"true"` // Describes the validator Validator validator.Validator `serialize:"true" json:"validator"` // ID of the Avalanche L1 this validator is validating Subnet ids.ID `serialize:"true" json:"subnet"` // Where to send staked tokens when done validating Stake []*avax.TransferableOutput `serialize:"true" json:"stake"` // Where to send staking rewards when done validating RewardsOwner fx.Owner `serialize:"true" json:"rewardsOwner"` } ``` #### New TypeIDs[​](#new-typeids "Direct link to heading") ``` ApricotProposalBlock = 0 ApricotAbortBlock = 1 ApricotCommitBlock = 2 ApricotStandardBlock = 3 ApricotAtomicBlock = 4 secp256k1fx.TransferInput = 5 secp256k1fx.MintOutput = 6 secp256k1fx.TransferOutput = 7 secp256k1fx.MintOperation = 8 secp256k1fx.Credential = 9 secp256k1fx.Input = 10 secp256k1fx.OutputOwners = 11 AddValidatorTx = 12 AddSubnetValidatorTx = 13 AddDelegatorTx = 14 CreateChainTx = 15 CreateSubnetTx = 16 ImportTx = 17 ExportTx = 18 AdvanceTimeTx = 19 RewardValidatorTx = 20 stakeable.LockIn = 21 stakeable.LockOut = 22 RemoveSubnetValidatorTx = 23 TransformSubnetTx = 24 AddPermissionlessValidatorTx = 25 AddPermissionlessDelegatorTx = 26 EmptyProofOfPossession = 27 BLSProofOfPossession = 28 BanffProposalBlock = 29 BanffAbortBlock = 30 BanffCommitBlock = 31 BanffStandardBlock = 32 ``` # Flow of a Single Blockchain (/docs/rpcs/other/guides/blockchain-flow) --- title: Flow of a Single Blockchain --- ![](/images/flow1.png) Intro[​](#intro "Direct link to heading") ----------------------------------------- The Avalanche network consists of 3 built-in blockchains: X-Chain, C-Chain, and P-Chain. The X-Chain is used to manage assets and uses the Avalanche consensus protocol. The C-Chain is used to create and interact with smart contracts and uses the Snowman consensus protocol. The P-Chain is used to coordinate validators and stake and also uses the Snowman consensus protocol. At the time of writing, the Avalanche network has ~1200 validators. A set of validators makes up an Avalanche L1. Avalanche L1s can validate 1 or more chains. It is a common misconception that 1 Avalanche L1 = 1 chain and this is shown by the primary Avalanche L1 of Avalanche which is made up of the X-Chain, C-Chain, and P-Chain. A node in the Avalanche network can either be a validator or a non-validator. A validator stakes AVAX tokens and participates in consensus to earn rewards. A non-validator does not participate in consensus or have any AVAX staked but can be used as an API server. Both validators and non-validators need to have their own copy of the chain and need to know the current state of the network. At the time of writing, there are ~1200 validators and ~1800 non-validators. Each blockchain on Avalanche has several components: the virtual machine, database, consensus engine, sender, and handler. These components help the chain run smoothly. Blockchains also interact with the P2P layer and the chain router to send and receive messages. Peer-to-Peer (P2P)[​](#peer-to-peer-p2p "Direct link to heading") ----------------------------------------------------------------- ### Outbound Messages[​](#outbound-messages "Direct link to heading") [The `OutboundMsgBuilder` interface](https://github.com/ava-labs/avalanchego/blob/master/message/outbound_msg_builder.go) specifies methods that build messages of type `OutboundMessage`. Nodes communicate to other nodes by sending `OutboundMessage` messages. All messaging functions in `OutboundMsgBuilder` can be categorized as follows: - **Handshake** - Nodes need to be on a certain version before they can be accepted into the network. - **State Sync** - A new node can ask other nodes for the current state of the network. It only syncs the required state for a specific block. - **Bootstrapping** - Nodes can ask other nodes for blocks to build their own copy of the chain. A node can fetch all blocks from the locally last accepted block to the current last accepted block in the network. - **Consensus** - Once a node is up to tip they can participate in consensus! During consensus, a node conducts a poll to several different small random samples of the validator set. They can communicate decisions on whether or not they have accepted/rejected a block. - **App** - VMs communicate application-specific messages to other nodes through app messages. A common example is mempool gossiping. Currently, AvalancheGo implements its own message serialization to communicate. In the future, AvalancheGo will use protocol buffers to communicate. ### Network[​](#network "Direct link to heading") [The networking interface](https://github.com/ava-labs/avalanchego/blob/master/network/network.go) is shared across all chains. It implements functions from the `ExternalSender` interface. The two functions it implements are `Send` and `Gossip`. `Send` sends a message of type `OutboundMessage` to a specific set of nodes (specified by an array of `NodeIDs`). `Gossip` sends a message of type `OutboundMessage` to a random group of nodes in an Avalanche L1 (can be a validator or a non-validator). Gossiping is used to push transactions across the network. The networking protocol uses TLS to pass messages between peers. Along with sending and gossiping, the networking library is also responsible for making connections and maintaining connections. Any node, either a validator or non-validator, will attempt to connect to the primary network. Router[​](#router "Direct link to heading") ------------------------------------------- [The `ChainRouter`](https://github.com/ava-labs/avalanchego/blob/master/snow/networking/router/chain_router.go) routes all incoming messages to its respective blockchain using `ChainID`. It does this by pushing all the messages onto the respective Chain handler's queue. The `ChainRouter` references all existing chains on the network such as the X-chain, C-chain, P-chain and possibly any other chain. The `ChainRouter` handles timeouts as well. When sending messages on the P2P layer, timeouts are registered on the sender and cleared on the `ChainRouter` side when a response is received. If no response is received, then it triggers a timeout. Because timeouts are handled on the `ChainRouter` side, the handler is reliable. Timeouts are triggered when peers do not respond and the `ChainRouter` will still notify the handler of failure cases. The timeout manager within `ChainRouter` is also adaptive. If the network is experiencing long latencies, timeouts will then be adjusted as well. Handler[​](#handler "Direct link to heading") --------------------------------------------- The main function of [the `Handler`](https://github.com/ava-labs/avalanchego/blob/master/snow/networking/handler/handler.go) is to pass messages from the network to the consensus engine. It receives these messages from the `ChainRouter`. It passes messages by pushing them onto a sync or Async queue (depends on message type). Messages are then popped from the queue, parsed, and routed to the correct function in consensus engine. This can be one of the following. - **State sync message (sync queue)** - **Bootstrapping message (sync queue)** - **Consensus message (sync queue)** - **App message (Async queue)** Sender[​](#sender "Direct link to heading") ------------------------------------------- The main role of [the `sender`](https://github.com/ava-labs/avalanchego/blob/master/snow/networking/sender/sender.go) is to build and send outbound messages. It is actually a very thin wrapper around the normal networking code. The main difference here is that sender registers timeouts and tells the router to expect a response message. The timer starts on the sender side. If there is no response, sender will send a failed response to the router. If a node is repeatedly unresponsive, that node will get benched and the sender will immediately start marking those messages as failed. If a sufficient amount of network deems the node benched, it might not get rewards (as a validator). Consensus Engine[​](#consensus-engine "Direct link to heading") --------------------------------------------------------------- Consensus is defined as getting a group of distributed systems to agree on an outcome. In the case of the Avalanche network, consensus is achieved when validators are in agreement with the state of the blockchain. The novel consensus algorithm is documented in the [white paper](https://assets.website-files.com/5d80307810123f5ffbb34d6e/6009805681b416f34dcae012_Avalanche%20Consensus%20Whitepaper.pdf). There are two main consensus algorithms: Avalanche and [Snowman](https://github.com/ava-labs/avalanchego/blob/master/snow/consensus/snowman/consensus.go). The engine is responsible for adding proposing a new block to consensus, repeatedly polling the network for decisions (accept/reject), and communicating that decision to the `Sender`. Blockchain Creation[​](#blockchain-creation "Direct link to heading") --------------------------------------------------------------------- [The `Manager`](https://github.com/ava-labs/avalanchego/blob/master/chains/manager.go) is what kick-starts everything in regards to blockchain creation, starting with the P-Chain. Once the P-Chain finishes bootstrapping, it will kickstart C-Chain and X-Chain and any other chains. The `Manager`'s job is not done yet, if a create-chain transaction is seen by a validator, a whole new process to create a chain will be started by the `Manager`. This can happen dynamically, long after the 3 chains in the Primary Network have been created and bootstrapped. # Issuing API Calls (/docs/rpcs/other/guides/issuing-api-calls) --- title: Issuing API Calls description: This guide explains how to make calls to APIs exposed by Avalanche nodes. --- Endpoints[​](#endpoints "Direct link to heading") ------------------------------------------------- An API call is made to an endpoint, which is a URL, made up of the base URI which is the address and the port of the node, and the path the particular endpoint the API call is on. ### Base URL[​](#base-url "Direct link to heading") The base of the URL is always: `[node-ip]:[http-port]` where - `node-ip` is the IP address of the node the call is to. - `http-port` is the port the node listens on for HTTP calls. This is specified by [command-line argument](/docs/nodes/configure/configs-flags#http-server) `http-port` (default value `9650`). For example, if you're making RPC calls on the local node, the base URL might look like this: `127.0.0.1:9650`. If you're making RPC calls to remote nodes, then the instead of `127.0.0.1` you should use the public IP of the server where the node is. Note that by default the node will only accept API calls on the local interface, so you will need to set up the [`http-host`](/docs/nodes/configure/configs-flags#--http-host-string) config flag on the node. Also, you will need to make sure the firewall and/or security policy allows access to the `http-port` from the internet. When setting up RPC access to a node, make sure you don't leave the `http-port` accessible to everyone! There are malicious actors that scan for nodes that have unrestricted access to their RPC port and then use those nodes for spamming them with resource-intensive queries which can knock the node offline. Only allow access to your node's RPC port from known IP addresses! ### Endpoint Path[​](#endpoint-path "Direct link to heading") Each API's documentation specifies what endpoint path a user should make calls to in order to access the API's methods. In general, they are formatted like: So for the Admin API, the endpoint path is `/ext/admin`, for the Info API it is `/ext/info` and so on. Note that some APIs have additional path components, most notably the chain RPC endpoints which includes the Avalanche L1 chain RPCs. We'll go over those in detail in the next section. So, in combining the base URL and the endpoint path we get the complete URL for making RPC calls. For example, to make a local RPC call on the Info API, the full URL would be: ``` http://127.0.0.1:9650/ext/info ``` Primary Network and Avalanche L1 RPC calls[​](#primary-network-and-avalanche-l1-rpc-calls "Direct link to heading") ------------------------------------------------------------------------------------------------------- Besides the APIs that are local to the node, like Admin or Metrics APIs, nodes also expose endpoints for talking to particular chains that are either part of the Primary Network (the X, P and C chains), or part of any Avalanche L1s the node might be syncing or validating. In general, chain endpoints are formatted as: ### Primary Network Endpoints[​](#primary-network-endpoints "Direct link to heading") The Primary Network consists of three chains: X, P and C chain. As those chains are present on every node, there are also convenient aliases defined that can be used instead of the full blockchainIDs. So, the endpoints look like: ### C-Chain and Subnet-EVM Endpoints[​](#c-chain-and-subnet-evm-endpoints "Direct link to heading") C-Chain and many Avalanche L1s run a version of the EthereumVM (EVM). EVM exposes its own endpoints, which are also accessible on the node: JSON-RPC, and Websocket. #### JSON-RPC EVM Endpoints[​](#json-rpc-evm-endpoints "Direct link to heading") To interact with C-Chain EVM via the JSON-RPC use the endpoint: To interact with Avalanche L1 instances of the EVM via the JSON-RPC endpoint: ``` /ext/bc/[blockchainID]/rpc ``` where `blockchainID` is the ID of the blockchain running the EVM. So for example, the RPC URL for the DFK Network (an Avalanche L1 that runs the DeFi Kingdoms:Crystalvale game) running on a local node would be: ``` http://127.0.0.1/ext/bc/q2aTwKuyzgs8pynF7UXBZCU7DejbZbZ6EUyHr3JQzYgwNPUPi/rpc ``` Or for the WAGMI Avalanche L1 on the Fuji testnet: ``` http://127.0.0.1/ext/bc/2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt/rpc ``` #### Websocket EVM Endpoints[​](#websocket-evm-endpoints "Direct link to heading") To interact with C-Chain via the websocket endpoint, use: To interact with other instances of the EVM via the websocket endpoint: where `blockchainID` is the ID of the blockchain running the EVM. For example, to interact with the C-Chain's Ethereum APIs via websocket on localhost you can use: ``` ws://127.0.0.1:9650/ext/bc/C/ws ``` When using the [Public API](/docs/rpcs) or another host that supports HTTPS, use `https://` or `wss://` instead of `http://` or `ws://`. Also, note that the [public API](/docs/rpcs#using-the-public-api-nodes) only supports C-Chain websocket API calls for API methods that don't exist on the C-Chain's HTTP API. Making a JSON RPC Request[​](#making-a-json-rpc-request "Direct link to heading") --------------------------------------------------------------------------------- Most of the built-in APIs use the [JSON RPC 2.0](https://www.jsonrpc.org/specification) format to describe their requests and responses. Such APIs include the Platform API and the X-Chain API. Suppose we want to call the `getTxStatus` method of the [X-Chain API](/docs/rpcs/x-chain). The X-Chain API documentation tells us that the endpoint for this API is `/ext/bc/X`. That means that the endpoint we send our API call to is: `[node-ip]:[http-port]/ext/bc/X` The X-Chain API documentation tells us that the signature of `getTxStatus` is: [`avm.getTxStatus`](/docs/rpcs/x-chain#avmgettxstatus)`(txID:bytes) -> (status:string)` where: - Argument `txID` is the ID of the transaction we're getting the status of. - Returned value `status` is the status of the transaction in question. To call this method, then: ``` curl -X POST --data '{ "jsonrpc":"2.0", "id" :4, "method" :"avm.getTxStatus", "params" :{ "txID":"2QouvFWUbjuySRxeX5xMbNCuAaKWfbk5FeEa2JmoF85RKLk2dD" } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/X ``` - `jsonrpc` specifies the version of the JSON RPC protocol. (In practice is always 2.0) - `method` specifies the service (`avm`) and method (`getTxStatus`) that we want to invoke. - `params` specifies the arguments to the method. - `id` is the ID of this request. Request IDs should be unique. That's it! ### JSON RPC Success Response[​](#json-rpc-success-response "Direct link to heading") If the call is successful, the response will look like this: ``` { "jsonrpc": "2.0", "result": { "Status": "Accepted" }, "id": 1 } ``` - `id` is the ID of the request that this response corresponds to. - `result` is the returned values of `getTxStatus`. ### JSON RPC Error Response[​](#json-rpc-error-response "Direct link to heading") If the API method invoked returns an error then the response will have a field `error` in place of `result`. Additionally, there is an extra field, `data`, which holds additional information about the error that occurred. Such a response would look like: ``` { "jsonrpc": "2.0", "error": { "code": -32600, "message": "[Some error message here]", "data": [Object with additional information about the error] }, "id": 1 } ``` Other API Formats[​](#other-api-formats "Direct link to heading") ----------------------------------------------------------------- Some APIs may use a standard other than JSON RPC 2.0 to format their requests and responses. Such extension should specify how to make calls and parse responses to them in their documentation. Sending and Receiving Bytes[​](#sending-and-receiving-bytes "Direct link to heading") ------------------------------------------------------------------------------------- Unless otherwise noted, when bytes are sent in an API call/response, they are in hex representation. However, Transaction IDs (TXIDs), ChainIDs, and subnetIDs are in [CB58](https://support.avalabs.org/en/articles/4587395-what-is-cb58) representation, a base-58 encoding with a checksum. # Transaction Fees (/docs/rpcs/other/guides/txn-fees) --- title: Transaction Fees --- In order to prevent spam, transactions on Avalanche require the payment of a transaction fee. The fee is paid in AVAX. **The transaction fee is burned (destroyed forever).** When you issue a transaction through Avalanche's API, the transaction fee is automatically deducted from one of the addresses you control. The [avalanchego wallet](https://github.com/ava-labs/avalanchego/blob/master/wallet/chain) contains example code written in golang for building and signing transactions on all three mainnet chains. X-Chain Fees[​](#fee-schedule) ------------------------------------------------------- The X-Chain currently operates under a fixed fee mechanism. This table shows the X-Chain transaction fee schedule: ``` +----------+---------------------------+--------------------------------+ | Chain | Transaction Type | Mainnet Transaction Fee (AVAX) | +----------+---------------------------+--------------------------------+ | X | Send | 0.001 | +----------+---------------------------+--------------------------------+ | X | Create Asset | 0.01 | +----------+---------------------------+--------------------------------+ | X | Mint Asset | 0.001 | +----------+---------------------------+--------------------------------+ | X | Import AVAX | 0.001 | +----------+---------------------------+--------------------------------+ | X | Export AVAX | 0.001 | +----------+---------------------------+--------------------------------+ ``` C-Chain Fees[​](#c-chain-fees) ------------------------------------------------------- The Avalanche C-Chain uses an algorithm to determine the "base fee" for a transaction. The base fee increases when network utilization is above the target utilization and decreases when network utilization is below the target. ### Dynamic Fee Transactions[​](#dynamic-fee-transactions ) Transaction fees for non-atomic transactions are based on Ethereum's EIP-1559 style Dynamic Fee Transactions, which consists of a gas fee cap and a gas tip cap. The fee cap specifies the maximum price the transaction is willing to pay per unit of gas. The tip cap (also called the priority fee) specifies the maximum amount above the base fee that the transaction is willing to pay per unit of gas. Therefore, the effective gas price paid by a transaction will be `min(gasFeeCap, baseFee + gasTipCap)`. Unlike in Ethereum, where the priority fee is paid to the miner that produces the block, in Avalanche both the base fee and the priority fee are burned. For legacy transactions, which only specify a single gas price, the gas price serves as both the gas fee cap and the gas tip cap. Use the [`eth_baseFee`](/docs/rpcs/c-chain#eth_basefee) API method to estimate the base fee for the next block. If more blocks are produced in between the time that you construct your transaction and it is included in a block, the base fee could be different from the base fee estimated by the API call, so it is important to treat this value as an estimate. Next, use [eth\_maxPriorityFeePerGas](/docs/rpcs/c-chain#eth_maxpriorityfeepergas) API call to estimate the priority fee needed to be included in a block. This API call will look at the most recent blocks and see what tips have been paid by recent transactions in order to be included in the block. Transactions are ordered by the priority fee, then the timestamp (oldest first). Based off of this information, you can specify the `gasFeeCap` and `gasTipCap` to your liking based on how you prioritize getting your transaction included as quickly as possible vs. minimizing the price paid per unit of gas. #### Base Fee[​](#base-fee) The base fee can go as low as 1 nAVAX (Gwei) and has no upper bound. You can use the [`eth_baseFee`](/docs/rpcs/c-chain#eth_basefee) and [eth\_maxPriorityFeePerGas](/docs/rpcs/c-chain#eth_maxpriorityfeepergas) API methods, or [Snowtrace's C-Chain Gas Tracker](https://snowtrace.io/gastracker), to estimate the gas price to use in your transactions. ### Atomic Transaction Fees[​](#atomic-transaction-fees) C-Chain atomic transactions (that is imports and exports from/to other chains) charge dynamic fees based on the amount of gas used by the transaction and the base fee of the block that includes the atomic transaction. Gas Used: ``` +---------------------+-------+ | Item : Gas | +---------------------+-------+ | Unsigned Tx Byte : 1 | +---------------------+-------+ | Signature : 1000 | +---------------------+-------+ | Per Atomic Tx : 10000 | +---------------------+-------+ ``` Therefore, the gas used by an atomic transaction is `1 * len(unsignedTxBytes) + 1,000 * numSignatures + 10,000` The TX fee additionally takes the base fee into account. Due to the fact that atomic transactions use units denominated in 9 decimal places, the base fee must be converted to 9 decimal places before calculating the actual fee paid by the transaction. Therefore, the actual fee is: `gasUsed * baseFee (converted to 9 decimals)`. P-Chain Fees[​](#p-chain-fees) ------------------------------------------------------- The Avalanche P-Chain utilizes a dynamic fee mechanism to optimize transaction costs and network utilization. This system adapts fees based on gas consumption to maintain a target utilization rate. ### Dimensions of Gas Consumption Gas consumption is measured across four dimensions: 1. **Bandwidth** The transaction size in bytes. 2. **Reads** The number of state/database reads. 3. **Writes** The number of state/database writes. 4. **Compute** The compute time in microseconds. The total gas consumed ($G$) by a transaction is: ```math G = B + 1000R + 1000W + 4C ``` The current fee dimension weight configurations as well as the parameter configurations of the P-Chain can be read at any time with the [`platform.getFeeConfig`](/docs/rpcs/p-chain#platformgetfeeconfig) API endpoint. ### Fee Adjustment Mechanism Fees adjust dynamically based on excess gas consumption, the difference between current gas usage and the target gas rate. The exponential adjustment ensures consistent reactivity regardless of the current gas price. Fee changes scale proportionally with excess gas consumption, maintaining fairness and network stability. The technical specification of this mechanism is documented in [ACP-103](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/103-dynamic-fees/README.md#mechanism). # X-Chain Migration (/docs/rpcs/other/guides/x-chain-migration) --- title: X-Chain Migration --- Overview[​](#overview "Direct link to heading") ----------------------------------------------- This document summarizes all of the changes made to the X-Chain API to support Avalanche Cortina (v1.10.0), which migrates the X-Chain to run Snowman++. In summary, the core transaction submission and confirmation flow is unchanged, however, there are new APIs that must be called to index all transactions. Transaction Broadcast and Confirmation[​](#transaction-broadcast-and-confirmation "Direct link to heading") ----------------------------------------------------------------------------------------------------------- The transaction format on the X-Chain does not change in Cortina. This means that wallets that have already integrated with the X-Chain don't need to change how they sign transactions. Additionally, there is no change to the format of the [avm.issueTx](/docs/rpcs/x-chain#avmissuetx) or the [avm.getTx](/docs/rpcs/x-chain#avmgettx) API. However, the [avm.getTxStatus](/docs/rpcs/x-chain#avmgettxstatus) endpoint is now deprecated and its usage should be replaced with [avm.getTx](/docs/rpcs/x-chain#avmgettx) (which only returns accepted transactions for AvalancheGo >= v1.9.12). [avm.getTxStatus](/docs/rpcs/x-chain#avmgettxstatus) will still work up to and after the Cortina activation if you wish to migrate after the network upgrade has occurred. Vertex -> Block Indexing[​](#vertex---block-indexing "Direct link to heading") ------------------------------------------------------------------------------ Before Cortina, indexing the X-Chain required polling the `/ext/index/X/vtx` endpoint to fetch new vertices. During the Cortina activation, a “stop vertex” will be produced using a [new codec version](https://github.com/ava-labs/avalanchego/blob/c27721a8da1397b218ce9e9ec69839b8a30f9860/snow/engine/avalanche/vertex/codec.go#L17-L18) that will contain no transactions. This new vertex type will be the [same format](https://github.com/ava-labs/avalanchego/blob/c27721a8da1397b218ce9e9ec69839b8a30f9860/snow/engine/avalanche/vertex/stateless_vertex.go#L95-L102) as previous vertices. To ensure historical data can still be accessed in Cortina, the `/ext/index/X/vtx` will remain accessible even though it will no longer be populated with chain data. The index for the X-chain tx and vtx endpoints will never increase again. The index for the X-chain blocks will increase as new blocks are added. After Cortina activation, you will need to migrate to using the new _ext/index/X/block_ endpoint (shares the same semantics as [/ext/index/P/block](/docs/rpcs/other/index-rpc#p-chain-blocks)) to continue indexing X-Chain activity. Because X-Chain ordering is deterministic in Cortina, this means that X-Chain blocks across all heights will be consistent across all nodes and will include a timestamp. Here is an example of iterating over these blocks in Golang: ``` package main import ( "context" "fmt" "log" "time" "github.com/ava-labs/avalanchego/indexer" "github.com/ava-labs/avalanchego/vms/proposervm/block" "github.com/ava-labs/avalanchego/wallet/chain/x" "github.com/ava-labs/avalanchego/wallet/subnet/primary" ) func main() { var ( uri = fmt.Sprintf("%s/ext/index/X/block", primary.LocalAPIURI) client = indexer.NewClient(uri) ctx = context.Background() nextIndex uint64 ) for { log.Printf("polling for next accepted block") container, err := client.GetContainerByIndex(ctx, nextIndex) if err != nil { time.Sleep(time.Second) continue } proposerVMBlock, err := block.Parse(container.Bytes) if err != nil { log.Fatalf("failed to parse proposervm block: %s\n", err) } avmBlockBytes := proposerVMBlock.Block() avmBlock, err := x.Parser.ParseBlock(avmBlockBytes) if err != nil { log.Fatalf("failed to parse avm block: %s\n", err) } acceptedTxs := avmBlock.Txs() log.Printf("accepted block %s with %d transactions", avmBlock.ID(), len(acceptedTxs)) for _, tx := range acceptedTxs { log.Printf("accepted transaction %s", tx.ID()) } nextIndex++ } } ``` After Cortina activation, it will also be possible to fetch X-Chain blocks directly without enabling the Index API. You can use the [avm.getBlock](/docs/rpcs/x-chain#avmgetblock), [avm.getBlockByHeight](/docs/rpcs/x-chain#avmgetblockbyheight), and [avm.getHeight](/docs/rpcs/x-chain#avmgetheight) endpoints to do so. This, again, will be similar to the [P-Chain semantics](/docs/rpcs/p-chain#platformgetblock). Deprecated API Calls[​](#deprecated-api-calls "Direct link to heading") ----------------------------------------------------------------------- This long-term deprecation effort will better align usage of AvalancheGo with its purpose, to be a minimal and efficient runtime that supports only what is required to validate the Primary Network and Avalanche L1s. Integrators should make plans to migrate to tools and services that are better optimized for serving queries over Avalanche Network state and avoid keeping any keys on the node itself. This deprecation ONLY applies to APIs that AvalancheGo exposes over the HTTP port. Transaction types with similar names to these APIs are NOT being deprecated. - ipcs - ipcs.publishBlockchain - ipcs.unpublishBlockchain - ipcs.getPublishedBlockchains - keystore - keystore.createUser - keystore.deleteUser - keystore.listUsers - keystore.importUser - keystore.exportUser - avm/pubsub - avm - avm.getAddressTxs - avm.getBalance - avm.getAllBalances - avm.createAsset - avm.createFixedCapAsset - avm.createVariableCapAsset - avm.createNFTAsset - avm.createAddress - avm.listAddresses - avm.exportKey - avm.importKey - avm.mint - avm.sendNFT - avm.mintNFT - avm.import - avm.export - avm.send - avm.sendMultiple - avm/wallet - wallet.issueTx - wallet.send - wallet.sendMultiple - platform - platform.exportKey - platform.importKey - platform.getBalance - platform.createAddress - platform.listAddresses - platform.getSubnets - platform.addValidator - platform.addDelegator - platform.addSubnetValidator - platform.createSubnet - platform.exportAVAX - platform.importAVAX - platform.createBlockchain - platform.getBlockchains - platform.getStake - platform.getMaxStakeAmount - platform.getRewardUTXOs Cortina FAQ[​](#cortina-faq "Direct link to heading") ----------------------------------------------------- ### Do I Have to Upgrade my Node?[​](#do-i-have-to-upgrade-my-node "Direct link to heading") If you don't upgrade your validator to `v1.10.0` before the Avalanche Mainnet activation date, your node will be marked as offline and other nodes will report your node as having lower uptime, which may jeopardize your staking rewards. ### Is There any Change in Hardware Requirements?[​](#is-there-any-change-in-hardware-requirements "Direct link to heading") No. ### Will Updating Decrease my Validator's Uptime?[​](#will-updating-decrease-my-validators-uptime "Direct link to heading") No. As a reminder, you can check your validator's estimated uptime using the [`info.uptime` API call](/docs/rpcs/other/info-rpc#infouptime). ### I Think Something Is Wrong. What Should I Do?[​](#i-think-something-is-wrong-what-should-i-do "Direct link to heading") First, make sure that you've read the documentation thoroughly and checked the [FAQs](https://support.avax.network/en/). If you don't see an answer to your question, go to our [Discord](https://discord.com/invite/RwXY7P6) server and search for your question. If it has not already been asked, please post it in the appropriate channel. # Avalanche Network Protocol (/docs/rpcs/other/standards/avalanche-network-protocol) --- title: Avalanche Network Protocol --- Overview[​](#overview "Direct link to heading") ----------------------------------------------- Avalanche network defines the core communication format between Avalanche nodes. It uses the [primitive serialization](/docs/rpcs/other/standards/serialization-primitives) format for payload packing. `"Containers"` are mentioned extensively in the description. A Container is simply a generic term for blocks. This document describes the protocol for peer-to-peer communication using Protocol Buffers (proto3). The protocol defines a set of messages exchanged between peers in a peer-to-peer network. Each message is represented by the `Message` proto message, which can encapsulate various types of messages, including network messages, state-sync messages, bootstrapping messages, consensus messages, and application messages. Message[​](#message "Direct link to heading") --------------------------------------------- The `Message` proto message is the main container for all peer-to-peer communication. It uses the `oneof` construct to represent different message types. The supported compression algorithms include Gzip and Zstd. ``` message Message { oneof message { bytes compressed_gzip = 1; bytes compressed_zstd = 2; // ... (other compression algorithms can be added) Ping ping = 11; Pong pong = 12; Version version = 13; PeerList peer_list = 14; // ... (other message types) } } ``` ### Compression[​](#compression "Direct link to heading") The `compressed_gzip` and `compressed_zstd` fields are used for Gzip and Zstd compression, respectively, of the encapsulated message. These fields are set only if the message type supports compression. Network Messages[​](#network-messages "Direct link to heading") --------------------------------------------------------------- ### Ping[​](#ping "Direct link to heading") The `Ping` message reports a peer's perceived uptime percentage. ``` message Ping { uint32 uptime = 1; repeated SubnetUptime subnet_uptimes = 2; } ``` - `uptime`: Uptime percentage on the primary network \[0, 100\]. - `subnet_uptimes`: Uptime percentages on Avalanche L1s. ### Pong[​](#pong "Direct link to heading") The `Pong` message is sent in response to a `Ping` with the perceived uptime of the peer. ``` message Pong { uint32 uptime = 1; // Deprecated: uptime is now sent in Ping repeated SubnetUptime subnet_uptimes = 2; // Deprecated: uptime is now sent in Ping } ``` ### Version[​](#version "Direct link to heading") The `Version` message is the first outbound message sent to a peer during the p2p handshake. ``` message Version { uint32 network_id = 1; uint64 my_time = 2; bytes ip_addr = 3; uint32 ip_port = 4; string my_version = 5; uint64 my_version_time = 6; bytes sig = 7; repeated bytes tracked_subnets = 8; } ``` - `network_id`: Network identifier (e.g., local, testnet, Mainnet). - `my_time`: Unix timestamp when the `Version` message was created. - `ip_addr`: IP address of the peer. - `ip_port`: IP port of the peer. - `my_version`: Avalanche client version. - `my_version_time`: Timestamp of the IP. - `sig`: Signature of the peer IP port pair at a provided timestamp. - `tracked_subnets`: Avalanche L1s the peer is tracking. ### PeerList[​](#peerlist "Direct link to heading") The `PeerList` message contains network-level metadata for a set of validators. ``` message PeerList { repeated ClaimedIpPort claimed_ip_ports = 1; } ``` - `claimed_ip_ports`: List of claimed IP and port pairs. ### PeerListAck[​](#peerlistack "Direct link to heading") The `PeerListAck` message is sent in response to `PeerList` to acknowledge the subset of peers that the peer will attempt to connect to. ``` message PeerListAck { reserved 1; // deprecated; used to be tx_ids repeated PeerAck peer_acks = 2; } ``` - `peer_acks`: List of acknowledged peers. State-Sync Messages[​](#state-sync-messages "Direct link to heading") --------------------------------------------------------------------- ### GetStateSummaryFrontier[​](#getstatesummaryfrontier "Direct link to heading") The `GetStateSummaryFrontier` message requests a peer's most recently accepted state summary. ``` message GetStateSummaryFrontier { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. ### StateSummaryFrontier[​](#statesummaryfrontier "Direct link to heading") The `StateSummaryFrontier` message is sent in response to a `GetStateSummaryFrontier` request. ``` message StateSummaryFrontier { bytes chain_id = 1; uint32 request_id = 2; bytes summary = 3; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `GetStateSummaryFrontier` request. - `summary`: The requested state summary. ### GetAcceptedStateSummary[​](#getacceptedstatesummary "Direct link to heading") The `GetAcceptedStateSummary` message requests a set of state summaries at specified block heights. ``` message GetAcceptedStateSummary { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; repeated uint64 heights = 4; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. - `heights`: Heights being requested. ### AcceptedStateSummary[​](#acceptedstatesummary "Direct link to heading") The `AcceptedStateSummary` message is sent in response to `GetAcceptedStateSummary`. ``` message AcceptedStateSummary { bytes chain_id = 1; uint32 request_id = 2; repeated bytes summary_ids = 3; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `GetAcceptedStateSummary` request. - `summary_ids`: State summary IDs. Bootstrapping Messages[​](#bootstrapping-messages "Direct link to heading") --------------------------------------------------------------------------- ### GetAcceptedFrontier[​](#getacceptedfrontier "Direct link to heading") The `GetAcceptedFrontier` message requests the accepted frontier from a peer. ``` message GetAcceptedFrontier { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; EngineType engine_type = 4; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. - `engine_type`: Consensus type the remote peer should use to handle this message. ### AcceptedFrontier[​](#acceptedfrontier "Direct link to heading") The `AcceptedFrontier` message contains the remote peer's last accepted frontier. ``` message AcceptedFrontier { reserved 4; // Until Cortina upgrade is activated bytes chain_id = 1; uint32 request_id = 2; bytes container_id = 3; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `GetAcceptedFrontier` request. - `container_id`: The ID of the last accepted frontier. ### GetAccepted[​](#getaccepted "Direct link to heading") The `GetAccepted` message sends a request with the sender's accepted frontier to a remote peer. ``` message GetAccepted { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; repeated bytes container_ids = 4; EngineType engine_type = 5; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this message. - `deadline`: Timeout (ns) for this request. - `container_ids`: The sender's accepted frontier. - `engine_type`: Consensus type to handle this message. ### Accepted[​](#accepted "Direct link to heading") The `Accepted` message is sent in response to `GetAccepted`. ``` message Accepted { reserved 4; // Until Cortina upgrade is activated bytes chain_id = 1; uint32 request_id = 2; repeated bytes container_ids = 3; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `GetAccepted` request. - `container_ids`: Subset of container IDs from the `GetAccepted` request that the sender has accepted. ### GetAncestors[​](#getancestors "Direct link to heading") The `GetAncestors` message requests the ancestors for a given container. ``` message GetAncestors { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; bytes container_id = 4; EngineType engine_type = 5; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. - `container_id`: Container for which ancestors are being requested. - `engine_type`: Consensus type to handle this message. ### Ancestors[​](#ancestors "Direct link to heading") The `Ancestors` message is sent in response to `GetAncestors`. ``` message Ancestors { reserved 4; // Until Cortina upgrade is activated bytes chain_id = 1; uint32 request_id = 2; repeated bytes containers = 3; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `GetAncestors` request. - `containers`: Ancestry for the requested container. Consensus Messages[​](#consensus-messages "Direct link to heading") ------------------------------------------------------------------- ### Get[​](#get "Direct link to heading") The `Get` message requests a container from a remote peer. ``` message Get { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; bytes container_id = 4; EngineType engine_type = 5; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. - `container_id`: Container being requested. - `engine_type`: Consensus type to handle this message. ### Put[​](#put "Direct link to heading") The `Put` message is sent in response to `Get` with the requested block. ``` message Put { bytes chain_id = 1; uint32 request_id = 2; bytes container = 3; EngineType engine_type = 4; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `Get` request. - `container`: Requested container. - `engine_type`: Consensus type to handle this message. ### PushQuery[​](#pushquery "Direct link to heading") The `PushQuery` message requests the preferences of a remote peer given a container. ``` message PushQuery { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; bytes container = 4; EngineType engine_type = 5; uint64 requested_height = 6; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. - `container`: Container being gossiped. - `engine_type`: Consensus type to handle this message. - `requested_height`: Requesting peer's last accepted height. ### PullQuery[​](#pullquery "Direct link to heading") The `PullQuery` message requests the preferences of a remote peer given a container id. ``` message PullQuery { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; bytes container_id = 4; EngineType engine_type = 5; uint64 requested_height = 6; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. - `container_id`: Container id being gossiped. - `engine_type`: Consensus type to handle this message. - `requested_height`: Requesting peer's last accepted height. ### Chits[​](#chits "Direct link to heading") The `Chits` message contains the preferences of a peer in response to a `PushQuery` or `PullQuery` message. ``` message Chits { bytes chain_id = 1; uint32 request_id = 2; bytes preferred_id = 3; bytes accepted_id = 4; bytes preferred_id_at_height = 5; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `PushQuery`/`PullQuery` request. - `preferred_id`: Currently preferred block. - `accepted_id`: Last accepted block. - `preferred_id_at_height`: Currently preferred block at the requested height. Application Messages[​](#application-messages "Direct link to heading") ----------------------------------------------------------------------- ### AppRequest[​](#apprequest "Direct link to heading") The `AppRequest` message is a VM-defined request. ``` message AppRequest { bytes chain_id = 1; uint32 request_id = 2; uint64 deadline = 3; bytes app_bytes = 4; } ``` - `chain_id`: Chain being requested from. - `request_id`: Unique identifier for this request. - `deadline`: Timeout (ns) for this request. - `app_bytes`: Request body. ### AppResponse[​](#appresponse "Direct link to heading") The `AppResponse` message is a VM-defined response sent in response to `AppRequest`. ``` message AppResponse { bytes chain_id = 1; uint32 request_id = 2; bytes app_bytes = 3; } ``` - `chain_id`: Chain being responded from. - `request_id`: Request ID of the original `AppRequest`. - `app_bytes`: Response body. ### AppGossip[​](#appgossip "Direct link to heading") The `AppGossip` message is a VM-defined message. ``` message AppGossip { bytes chain_id = 1; bytes app_bytes = 2; } ``` - `chain_id`: Chain the message is for. - `app_bytes`: Message body. # Cryptographic Primitives (/docs/rpcs/other/standards/cryptographic-primitives) --- title: Cryptographic Primitives --- Avalanche uses a variety of cryptographic primitives for its different functions. This file summarizes the type and kind of cryptography used at the network and blockchain layers. ## Cryptography in the Network Layer Avalanche uses Transport Layer Security, TLS, to protect node-to-node communications from eavesdroppers. TLS combines the practicality of public-key cryptography with the efficiency of symmetric-key cryptography. This has resulted in TLS becoming the standard for internet communication. Whereas most classical consensus protocols employ public-key cryptography to prove receipt of messages to third parties, the novel Snow\* consensus family does not require such proofs. This enables Avalanche to employ TLS in authenticating stakers and eliminates the need for costly public-key cryptography for signing network messages. ### TLS Certificates Avalanche does not rely on any centralized third-parties, and in particular, it does not use certificates issued by third-party authenticators. All certificates used within the network layer to identify endpoints are self-signed, thus creating a self-sovereign identity layer. No third parties are ever involved. ### TLS Addresses To avoid posting the full TLS certificate to the P-Chain, the certificate is first hashed. For consistency, Avalanche employs the same hashing mechanism for the TLS certificates as is used in Bitcoin. Namely, the DER representation of the certificate is hashed with sha256, and the result is then hashed with ripemd160 to yield a 20-byte identifier for stakers. This 20-byte identifier is represented by "NodeID-" followed by the data's [CB58](https://support.avalabs.org/en/articles/4587395-what-is-cb58) encoded string. ## Cryptography in the Avalanche Virtual Machine The Avalanche virtual machine uses elliptic curve cryptography, specifically `secp256k1`, for its signatures on the blockchain. This 32-byte identifier is represented by "PrivateKey-" followed by the data's [CB58](https://support.avalabs.org/en/articles/4587395-what-is-cb58) encoded string. ### Secp256k1 Addresses Avalanche is not prescriptive about addressing schemes, choosing to instead leave addressing up to each blockchain. The addressing scheme of the X-Chain and the P-Chain relies on secp256k1. Avalanche follows a similar approach as Bitcoin and hashes the ECDSA public key. The 33-byte compressed representation of the public key is hashed with sha256 **once**. The result is then hashed with ripemd160 to yield a 20-byte address. Avalanche uses the convention `chainID-address` to specify which chain an address exists on. `chainID` may be replaced with an alias of the chain. When transmitting information through external applications, the CB58 convention is required. ### Bech32 Addresses on the X-Chain and P-Chain use the [Bech32](http://support.avalabs.org/en/articles/4587392-what-is-bech32) standard outlined in [BIP 0173](https://en.bitcoin.it/wiki/BIP_0173). There are four parts to a Bech32 address scheme. In order of appearance: - A human-readable part (HRP). On Mainnet this is `avax`. - The number `1`, which separates the HRP from the address and error correction code. - A base-32 encoded string representing the 20 byte address. - A 6-character base-32 encoded error correction code. Additionally, an Avalanche address is prefixed with the alias of the chain it exists on, followed by a dash. For example, X-Chain addresses are prefixed with `X-`. The following regular expression matches addresses on the X-Chain, P-Chain and C-Chain for Mainnet, Fuji and localhost. Note that all valid Avalanche addresses will match this regular expression, but some strings that are not valid Avalanche addresses may match this regular expression. ``` ^([XPC]|[a-km-zA-HJ-NP-Z1-9]{36,72})-[a-zA-Z]{1,83}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}$ ``` Read more about Avalanche's [addressing scheme](https://support.avalabs.org/en/articles/4596397-what-is-an-address). For example the following Bech32 address, `X-avax19rknw8l0grnfunjrzwxlxync6zrlu33y2jxhrg`, is composed like so: 1. HRP: `avax` 2. Separator: `1` 3. Address: `9rknw8l0grnfunjrzwxlxync6zrlu33y` 4. Checksum: `2jxhrg` Depending on the `networkID`, the encoded addresses will have a distinctive HRP per each network. - 0 - X-`custom`19rknw8l0grnfunjrzwxlxync6zrlu33yeg5dya - 1 - X-`avax`19rknw8l0grnfunjrzwxlxync6zrlu33y2jxhrg - 2 - X-`cascade`19rknw8l0grnfunjrzwxlxync6zrlu33ypmtvnh - 3 - X-`denali`19rknw8l0grnfunjrzwxlxync6zrlu33yhc357h - 4 - X-`everest`19rknw8l0grnfunjrzwxlxync6zrlu33yn44wty - 5 - X-`fuji`19rknw8l0grnfunjrzwxlxync6zrlu33yxqzg0h - 1337 - X-`custom`19rknw8l0grnfunjrzwxlxync6zrlu33yeg5dya - 12345 - X-`local`19rknw8l0grnfunjrzwxlxync6zrlu33ynpm3qq Here's the mapping of `networkID` to bech32 HRP. ``` 0: "custom", 1: "avax", 2: "cascade", 3: "denali", 4: "everest", 5: "fuji", 1337: "custom", 12345: "local" ``` ### Secp256k1 Recoverable Signatures Recoverable signatures are stored as the 65-byte **`[R || S || V]`** where **`V`** is 0 or 1 to allow quick public key recoverability. **`S`** must be in the lower half of the possible range to prevent signature malleability. Before signing a message, the message is hashed using sha256. ### Secp256k1 Example Suppose Rick and Morty are setting up a secure communication channel. Morty creates a new public-private key pair. Private Key: `0x98cb077f972feb0481f1d894f272c6a1e3c15e272a1658ff716444f465200070` Public Key (33-byte compressed): `0x02b33c917f2f6103448d7feb42614037d05928433cb25e78f01a825aa829bb3c27` Because of Rick's infinite wisdom, he doesn't trust himself with carrying around Morty's public key, so he only asks for Morty's address. Morty follows the instructions, SHA256's his public key, and then ripemd160's that result to produce an address. SHA256(Public Key): `0x28d7670d71667e93ff586f664937f52828e6290068fa2a37782045bffa7b0d2f` Address: `0xe8777f38c88ca153a6fdc25942176d2bf5491b89` Morty is quite confused because a public key should be safe to be public knowledge. Rick belches and explains that hashing the public key protects the private key owner from potential future security flaws in elliptic curve cryptography. In the event cryptography is broken and a private key can be derived from a public key, users can transfer their funds to an address that has never signed a transaction before, preventing their funds from being compromised by an attacker. This enables coin owners to be protected while the cryptography is upgraded across the clients. Later, once Morty has learned more about Rick's backstory, Morty attempts to send Rick a message. Morty knows that Rick will only read the message if he can verify it was from him, so he signs the message with his private key. Message: `0x68656c702049276d207472617070656420696e206120636f6d7075746572` Message Hash: `0x912800c29d554fb9cdce579c0abba991165bbbc8bfec9622481d01e0b3e4b7da` Message Signature: `0xb52aa0535c5c48268d843bd65395623d2462016325a86f09420c81f142578e121d11bd368b88ca6de4179a007e6abe0e8d0be1a6a4485def8f9e02957d3d72da01` Morty was never seen again. ### Signed Messages A standard for interoperable generic signed messages based on the Bitcoin Script format and Ethereum format. ``` sign(sha256(length(prefix) + prefix + length(message) + message)) ``` The prefix is simply the string `\x1AAvalanche Signed Message:\n`, where `0x1A` is the length of the prefix text and `length(message)` is an [integer](/docs/rpcs/other/standards/serialization-primitives#integer) of the message size. ### Gantt Pre-Image Specification ``` +---------------+-----------+------------------------------+ | prefix : [26]byte | 26 bytes | +---------------+-----------+------------------------------+ | messageLength : int | 4 bytes | +---------------+-----------+------------------------------+ | message : []byte | size(message) bytes | +---------------+-----------+------------------------------+ | 26 + 4 + size(message) | +------------------------------+ ``` ### Example As an example we will sign the message "Through consensus to the stars" ``` // prefix size: 26 bytes 0x1a // prefix: Avalanche Signed Message:\n 0x41 0x76 0x61 0x6c 0x61 0x6e 0x63 0x68 0x65 0x20 0x53 0x69 0x67 0x6e 0x65 0x64 0x20 0x4d 0x65 0x73 0x73 0x61 0x67 0x65 0x3a 0x0a // msg size: 30 bytes 0x00 0x00 0x00 0x1e // msg: Through consensus to the stars 54 68 72 6f 75 67 68 20 63 6f 6e 73 65 6e 73 75 73 20 74 6f 20 74 68 65 20 73 74 61 72 73 ``` After hashing with `sha256` and signing the pre-image we return the value [cb58](https://support.avalabs.org/en/articles/4587395-what-is-cb58) encoded: `4Eb2zAHF4JjZFJmp4usSokTGqq9mEGwVMY2WZzzCmu657SNFZhndsiS8TvL32n3bexd8emUwiXs8XqKjhqzvoRFvghnvSN`. Here's an example using [Core web](https://core.app/tools/signing-tools/sign/). A full guide on how to sign messages with Core web can be found [here](https://support.avax.network/en/articles/7206948-core-web-how-do-i-use-the-signing-tools). ![Sign message](/images/cryptography1.png) ## Cryptography in Ethereum Virtual Machine Avalanche nodes support the full Ethereum Virtual Machine (EVM) and precisely duplicate all of the cryptographic constructs used in Ethereum. This includes the Keccak hash function and the other mechanisms used for cryptographic security in the EVM. ## Cryptography in Other Virtual Machines Since Avalanche is an extensible platform, we expect that people will add additional cryptographic primitives to the system over time. # Serialization Primitives (/docs/rpcs/other/standards/serialization-primitives) --- title: Serialization Primitives --- Avalanche uses a simple, uniform, and elegant representation for all internal data. This document describes how primitive types are encoded on the Avalanche platform. Transactions are encoded in terms of these basic primitive types. Byte[​](#byte "Direct link to heading") --------------------------------------- Bytes are packed as-is into the message payload. Example: ``` Packing: 0x01 Results in: [0x01] ``` Short[​](#short "Direct link to heading") ----------------------------------------- Shorts are packed in BigEndian format into the message payload. Example: ``` Packing: 0x0102 Results in: [0x01, 0x02] ``` Integer[​](#integer "Direct link to heading") --------------------------------------------- Integers are 32-bit values packed in BigEndian format into the message payload. Example: ``` Packing: 0x01020304 Results in: [0x01, 0x02, 0x03, 0x04] ``` Long Integers[​](#long-integers "Direct link to heading") --------------------------------------------------------- Long integers are 64-bit values packed in BigEndian format into the message payload. Example: ``` Packing: 0x0102030405060708 Results in: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] ``` IP Addresses[​](#ip-addresses "Direct link to heading") ------------------------------------------------------- IP addresses are represented as 16-byte IPv6 format, with the port appended into the message payload as a Short. IPv4 addresses are padded with 12 bytes of leading 0x00s. IPv4 example: ``` Packing: "127.0.0.1:9650" Results in: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, 0x25, 0xb2, ] ``` IPv6 example: ``` Packing: "[2001:0db8:ac10:fe01::]:12345" Results in: [ 0x20, 0x01, 0x0d, 0xb8, 0xac, 0x10, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, ] ``` Fixed-Length Array[​](#fixed-length-array "Direct link to heading") ------------------------------------------------------------------- Fixed-length arrays, whose length is known ahead of time and by context, are packed in order. Byte array example: ``` Packing: [0x01, 0x02] Results in: [0x01, 0x02] ``` Integer array example: ``` Packing: [0x03040506] Results in: [0x03, 0x04, 0x05, 0x06] ``` Variable Length Array[​](#variable-length-array "Direct link to heading") ------------------------------------------------------------------------- The length of the array is prefixed in Integer format, followed by the packing of the array contents in Fixed Length Array format. Byte array example: ``` Packing: [0x01, 0x02] Results in: [0x00, 0x00, 0x00, 0x02, 0x01, 0x02] ``` Int array example: ``` Packing: [0x03040506] Results in: [0x00, 0x00, 0x00, 0x01, 0x03, 0x04, 0x05, 0x06] ``` String[​](#string "Direct link to heading") ------------------------------------------- A String is packed similarly to a variable-length byte array. However, the length prefix is a short rather than an int. Strings are encoded in UTF-8 format. Example: ``` Packing: "Avax" Results in: [0x00, 0x04, 0x41, 0x76, 0x61, 0x78] ``` # Deploy Custom VM (/docs/tooling/avalanche-cli/create-avalanche-nodes/deploy-custom-vm) --- title: Deploy Custom VM description: This page demonstrates how to deploy a custom VM into cloud-based validators using Avalanche-CLI. --- Currently, only Fuji network and Devnets are supported. ALPHA WARNING: This command is currently in experimental mode. Proceed at your own risk. ## Prerequisites Before we begin, you will need to have: - Created a cloud server node as described [here](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-aws) - Created a Custom VM, as described [here](/docs/primary-network/virtual-machines). - (Ignore for Devnet) Set up a key to be able to pay for transaction Fees, as described [here](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet). Currently, only AWS & GCP cloud services are supported. Deploying the VM[​](#deploying-the-vm "Direct link to heading") --------------------------------------------------------------- We will be deploying the [MorpheusVM](https://github.com/ava-labs/hypersdk/tree/main/examples/morpheusvm) example built with the HyperSDK. The following settings will be used: - Repo url: `https://github.com/ava-labs/hypersdk/` - Branch Name: `vryx-poc` - Build Script: `examples/morpheusvm/scripts/build.sh` The CLI needs a public repo url in order to be able to download and install the custom VM on cloud. ### Genesis File[​](#genesis-file "Direct link to heading") The following contents will serve as the chain genesis. They were generated using `morpheus-cli` as shown [here](https://github.com/ava-labs/hypersdk/blob/main/examples/morpheusvm/scripts/run.sh). Save it into a file with path `` (for example `~/morpheusvm_genesis.json`): ```bash { "stateBranchFactor":16, "minBlockGap":1000, "minUnitPrice":[1,1,1,1,1], "maxChunkUnits":[1800000,18446744073709551615,18446744073709551615,18446744073709551615,18446744073709551615], "epochDuration":60000, "validityWindow":59000, "partitions":8, "baseUnits":1, "baseWarpUnits":1024, "warpUnitsPerSigner":128, "outgoingWarpComputeUnits":1024, "storageKeyReadUnits":5, "storageValueReadUnits":2, "storageKeyAllocateUnits":20, "storageValueAllocateUnits":5, "storageKeyWriteUnits":10, "storageValueWriteUnits":3, "customAllocation": [ { "address":"morpheus1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdjk97rwu", "balance":3000000000000000000 }, {"address":"morpheus1qryyvfut6td0l2vwn8jwae0pmmev7eqxs2vw0fxpd2c4lr37jj7wvrj4vc3", "balance":3000000000000000000 }, {"address":"morpheus1qp52zjc3ul85309xn9stldfpwkseuth5ytdluyl7c5mvsv7a4fc76g6c4w4", "balance":3000000000000000000 }, {"address":"morpheus1qzqjp943t0tudpw06jnvakdc0y8w790tzk7suc92aehjw0epvj93s0uzasn", "balance":3000000000000000000 }, {"address":"morpheus1qz97wx3vl3upjuquvkulp56nk20l3jumm3y4yva7v6nlz5rf8ukty8fh27r", "balance":3000000000000000000 } ] } ``` Create the Avalanche L1[​](#create-the-avalanche-l1 "Direct link to heading") ----------------------------------------------------------------- Let's create an Avalanche L1 called ``, with custom VM binary and genesis. ```bash avalanche blockchain create ``` Choose custom ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose your VM: Subnet-EVM ▸ Custom ``` Provide path to genesis: ```bash ✗ Enter path to custom genesis: ``` Provide the source code repo url: ```bash ✗ Source code repository URL: https://github.com/ava-labs/hypersdk/ ``` Set the branch and finally set the build script: ```bash ✗ Build script: examples/morpheusvm/scripts/build.sh ``` CLI will generate a locally compiled binary, and then create the Avalanche L1. ```bash Cloning into ... Successfully created subnet configuration ``` ## Deploy Avalanche L1 For this example, we will deploy the Avalanche L1 and blockchain on Fuji. Run: ```bash avalanche blockchain deploy ``` Choose Fuji: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to deploy on: Local Network ▸ Fuji Mainnet ``` Use the stored key: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Which key source should be used to pay transaction fees?: ▸ Use stored key Use ledger ``` Choose `` as the key to use to pay the fees: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Which stored key should be used to pay transaction fees?: ▸ ``` Use the same key as the control key for the Avalanche L1: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? How would you like to set your control keys?: ▸ Use fee-paying key Use all stored keys Custom list ``` The successfully creation of our Avalanche L1 and blockchain is confirmed by the following output: ```bash Your Subnet's control keys: [P-fuji1dlwux652lkflgz79g3nsphjzvl6t35xhmunfk1] Your subnet auth keys for chain creation: [P-fuji1dlwux652lkflgz79g3nsphjzvl6t35xhmunfk1] Subnet has been created with ID: RU72cWmBmcXber6ZBPT7R5scFFuVSoFRudcS3vayf3L535ZE3 Now creating blockchain... +--------------------+----------------------------------------------------+ | DEPLOYMENT RESULTS | | +--------------------+----------------------------------------------------+ | Chain Name | blockchainName | +--------------------+----------------------------------------------------+ | Subnet ID | RU72cWmBmcXber6ZBPT7R5scFFuVSoFRudcS3vayf3L535ZE3 | +--------------------+----------------------------------------------------+ | VM ID | srEXiWaHq58RK6uZMmUNaMF2FzG7vPzREsiXsptAHk9gsZNvN | +--------------------+----------------------------------------------------+ | Blockchain ID | 2aDgZRYcSBsNoLCsC8qQH6iw3kUSF5DbRHM4sGEqVKwMSfBDRf | +--------------------+ + | P-Chain TXID | | +--------------------+----------------------------------------------------+ ``` Set the Config Files[​](#set-the-config-files "Direct link to heading") ----------------------------------------------------------------------- Avalanche-CLI supports uploading the full set of configuration files for a blockchain: - Genesis File - Blockchain Config - Avalanche L1 Config - Network Upgrades - AvalancheGo Config The following example uses all of them, but the user can decide to provide a subset of those. ### AvalancheGo Flags[​](#avalanchego-flags "Direct link to heading") Save the following content (as defined [here](https://github.com/ava-labs/hypersdk/blob/vryx-poc/examples/morpheusvm/tests/e2e/e2e_test.go)) into a file with path `` (for example `~/morpheusvm_avago.json`): ```json { "log-level":"INFO", "log-display-level":"INFO", "proposervm-use-current-height":true, "throttler-inbound-validator-alloc-size":"10737418240", "throttler-inbound-at-large-alloc-size":"10737418240", "throttler-inbound-node-max-processing-msgs":"1000000", "throttler-inbound-node-max-at-large-bytes":"10737418240", "throttler-inbound-bandwidth-refill-rate":"1073741824", "throttler-inbound-bandwidth-max-burst-size":"1073741824", "throttler-inbound-cpu-validator-alloc":"100000", "throttler-inbound-cpu-max-non-validator-usage":"100000", "throttler-inbound-cpu-max-non-validator-node-usage":"100000", "throttler-inbound-disk-validator-alloc":"10737418240000", "throttler-outbound-validator-alloc-size":"10737418240", "throttler-outbound-at-large-alloc-size":"10737418240", "throttler-outbound-node-max-at-large-bytes":"10737418240", "consensus-on-accept-gossip-validator-size":"10", "consensus-on-accept-gossip-peer-size":"10", "network-compression-type":"zstd", "consensus-app-concurrency":"128", "profile-continuous-enabled":true, "profile-continuous-freq":"1m", "http-host":"", "http-allowed-origins": "*", "http-allowed-hosts": "*" } ``` Then set the Avalanche L1 to use it by executing: ```bash avalanche blockchain configure blockchainName ``` Select node-config.json: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Which configuration file would you like to provide?: ▸ node-config.json chain.json subnet.json per-node-chain.json ``` Provide the path to the AvalancheGo config file: ```bash ✗ Enter the path to your configuration file: ``` Finally, choose no: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Would you like to provide the chain.json file as well?: ▸ No Yes File ~/.avalanche-cli/subnets/blockchainName/node-config.json successfully written ``` ### Blockchain Config[​](#blockchain-config "Direct link to heading") `morpheus-cli` as shown [here](https://github.com/ava-labs/hypersdk/blob/vryx-poc/examples/morpheusvm/scripts/run.sh). Save the following content (generated by this [script](https://github.com/ava-labs/hypersdk/blob/vryx-poc/examples/morpheusvm/scripts/run.sh)) in a known file path (for example `~/morpheusvm_chain.json`): ```json { "chunkBuildFrequency": 250, "targetChunkBuildDuration": 250, "blockBuildFrequency": 100, "mempoolSize": 2147483648, "mempoolSponsorSize": 10000000, "authExecutionCores": 16, "precheckCores": 16, "actionExecutionCores": 8, "missingChunkFetchers": 48, "verifyAuth": true, "authRPCCores": 48, "authRPCBacklog": 10000000, "authGossipCores": 16, "authGossipBacklog": 10000000, "chunkStorageCores": 16, "chunkStorageBacklog": 10000000, "streamingBacklogSize": 10000000, "continuousProfilerDir":"/home/ubuntu/morpheusvm-profiles", "logLevel": "INFO" } ``` Then set the Avalanche L1 to use it by executing: ```bash avalanche blockchain configure blockchainName ``` Select chain.json: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Which configuration file would you like to provide?: node-config.json ▸ chain.json subnet.json per-node-chain.json ``` Provide the path to the blockchain config file: ```bash ✗ Enter the path to your configuration file: ~/morpheusvm_chain.json ``` Finally choose no: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Would you like to provide the subnet.json file as well?: ▸ No Yes File ~/.avalanche-cli/subnets/blockchainName/chain.json successfully written ``` ### Avalanche L1 Config[​](#avalanche-l1-config "Direct link to heading") Save the following content (generated by this [script](https://github.com/ava-labs/hypersdk/blob/vryx-poc/examples/morpheusvm/scripts/run.sh)) in a known path (for example `~/morpheusvm_subnet.json`): ```json { "proposerMinBlockDelay": 0, "proposerNumHistoricalBlocks": 512 } ``` Then set the Avalanche L1 to use it by executing: ```bash avalanche blockchain configure blockchainName ``` Select `subnet.json`: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Which configuration file would you like to provide?: node-config.json chain.json ▸ subnet.json per-node-chain.json ``` Provide the path to the Avalanche L1 config file: ```bash ✗ Enter the path to your configuration file: ~/morpheusvm_subnet.json ``` Choose no: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Would you like to provide the chain.json file as well?: ▸ No Yes File ~/.avalanche-cli/subnets/blockchainName/subnet.json successfully written ``` ### Network Upgrades[​](#network-upgrades "Direct link to heading") Save the following content (currently with no network upgrades) in a known path (for example `~/morpheusvm_upgrades.json`): Then set the Avalanche L1 to use it by executing: ```bash avalanche blockchain upgrade import blockchainName ``` Provide the path to the network upgrades file: ```bash ✗ Provide the path to the upgrade file to import: ~/morpheusvm_upgrades.json ``` Deploy Our Custom VM[​](#deploy-our-custom-vm "Direct link to heading") ----------------------------------------------------------------------- To deploy our Custom VM, run: ```bash avalanche node sync ``` ```bash Node(s) successfully started syncing with Subnet! ``` Your custom VM is successfully deployed! You can also use `avalanche node update blockchain ` to reinstall the binary when the branch is updated, or update the config files. # Execute SSH Command (/docs/tooling/avalanche-cli/create-avalanche-nodes/execute-ssh-commands) --- title: Execute SSH Command description: This page demonstrates how to execute a SSH command on a Cluster or Node managed by Avalanche-CLI --- ALPHA WARNING: This command is currently in experimental mode. Proceed at your own risk. ## Prerequisites Before we begin, you will need to have a cluster managed by CLI, either a [Fuji Cluster using AWS](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-aws), a [Fuji Cluster using GCP](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-gcp), or a [Devnet](/docs/tooling/avalanche-cli/create-avalanche-nodes/setup-devnet), SSH Warning[​](#ssh-warning "Direct link to heading") ----------------------------------------------------- Note: An expected warning may be seen when executing the command on a given cluster for the first time: ```bash Warning: Permanently added 'IP' (ED25519) to the list of known hosts. ``` Get SSH Connection Instructions for All Clusters[​](#get-ssh-connection-instructions-for-all-clusters "Direct link to heading") ------------------------------------------------------------------------------------------------------------------------------- Just execute `node ssh`: ```bash avalanche node ssh Cluster "" (Devnet) [i-0cf58a280bf3ef9a1] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem [i-0e2abd71a586e56b4] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem [i-027417a4f2ca0a478] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem [i-0360a867aa295d8a4] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem [i-0759b102acfd5b585] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem ``` Get the AvalancheGo PID for All Nodes in ``[​](#get-the-avalanchego-pid-for-all-nodes-in-clustername "Direct link to heading") ------------------------------------------------------------------------------------------------------------------------------------------- ```bash avalanche node ssh pgrep avalanchego [i-0cf58a280bf3ef9a1] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem pgrep avalanchego 14508 [i-0e2abd71a586e56b4] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem pgrep avalanchego 14555 [i-027417a4f2ca0a478] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem pgrep avalanchego 14545 [i-0360a867aa295d8a4] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem pgrep avalanchego 14531 [i-0759b102acfd5b585] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem pgrep avalanchego 14555 ``` Please note that commands via `ssh` on cluster are executed sequentially by default. It's possible to run command on all nodes at the same time by using `--parallel=true` flag Get the AvalancheGo Configuration for All Nodes in ``[​](#get-the-avalanchego-configuration-for-all-nodes-in-clustername "Direct link to heading") --------------------------------------------------------------------------------------------------------------------------------------------------------------- ```bash avalanche node ssh cat /home/ubuntu/.avalanchego/configs/node.json [i-0cf58a280bf3ef9a1] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem cat /home/ubuntu/.avalanchego/configs/node.json { "bootstrap-ids": "", "bootstrap-ips": "", "genesis-file": "/home/ubuntu/.avalanchego/configs/genesis.json", "http-allowed-hosts": "*", "http-allowed-origins": "*", "http-host": "", "log-display-level": "info", "log-level": "info", "network-id": "network-1338", "public-ip": "44.219.113.190", "track-subnets": "giY8tswWgZmcAWzPkoNrmjjrykited7GJ9799SsFzTiq5a1ML" } [i-0e2abd71a586e56b4] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem cat /home/ubuntu/.avalanchego/configs/node.json { "bootstrap-ids": "NodeID-EzxsrhoumLsQSWxsohfMFrM1rJcaiaBK8", "bootstrap-ips": "44.219.113.190:9651", "genesis-file": "/home/ubuntu/.avalanchego/configs/genesis.json", "http-allowed-hosts": "*", "http-allowed-origins": "*", "http-host": "", "log-display-level": "info", "log-level": "info", "network-id": "network-1338", "public-ip": "3.212.206.161", "track-subnets": "giY8tswWgZmcAWzPkoNrmjjrykited7GJ9799SsFzTiq5a1ML" } [i-027417a4f2ca0a478] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem cat /home/ubuntu/.avalanchego/configs/node.json { "bootstrap-ids": "NodeID-EzxsrhoumLsQSWxsohfMFrM1rJcaiaBK8,NodeID-6veKG5dAz1uJvKc7qm7v6wAPDod8hctb9", "bootstrap-ips": "44.219.113.190:9651,3.212.206.161:9651", "genesis-file": "/home/ubuntu/.avalanchego/configs/genesis.json", "http-allowed-hosts": "*", "http-allowed-origins": "*", "http-host": "", "log-display-level": "info", "log-level": "info", "network-id": "network-1338", "public-ip": "54.87.168.26", "track-subnets": "giY8tswWgZmcAWzPkoNrmjjrykited7GJ9799SsFzTiq5a1ML" } [i-0360a867aa295d8a4] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem cat /home/ubuntu/.avalanchego/configs/node.json { "bootstrap-ids": "NodeID-EzxsrhoumLsQSWxsohfMFrM1rJcaiaBK8,NodeID-6veKG5dAz1uJvKc7qm7v6wAPDod8hctb9,NodeID-ASseyUweBT82XquiGpmUFjd9QfkUjxiAY", "bootstrap-ips": "44.219.113.190:9651,3.212.206.161:9651,54.87.168.26:9651", "genesis-file": "/home/ubuntu/.avalanchego/configs/genesis.json", "http-allowed-hosts": "*", "http-allowed-origins": "*", "http-host": "", "log-display-level": "info", "log-level": "info", "network-id": "network-1338", "public-ip": "3.225.42.57", "track-subnets": "giY8tswWgZmcAWzPkoNrmjjrykited7GJ9799SsFzTiq5a1ML" } [i-0759b102acfd5b585] ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no [email protected] -i /home/fm/.ssh/fm-us-east-1-avalanche-cli-us-east-1-kp.pem cat /home/ubuntu/.avalanchego/configs/node.json { "bootstrap-ids": "NodeID-EzxsrhoumLsQSWxsohfMFrM1rJcaiaBK8,NodeID-6veKG5dAz1uJvKc7qm7v6wAPDod8hctb9,NodeID-ASseyUweBT82XquiGpmUFjd9QfkUjxiAY,NodeID-LfwbUp9dkhmWTSGffer9kNWNzqUQc2TEJ", "bootstrap-ips": "44.219.113.190:9651,3.212.206.161:9651,54.87.168.26:9651,3.225.42.57:9651", "genesis-file": "/home/ubuntu/.avalanchego/configs/genesis.json", "http-allowed-hosts": "*", "http-allowed-origins": "*", "http-host": "", "log-display-level": "info", "log-level": "info", "network-id": "network-1338", "public-ip": "107.21.158.224", "track-subnets": "giY8tswWgZmcAWzPkoNrmjjrykited7GJ9799SsFzTiq5a1ML" } ``` Executing Command on a Single Node[​](#executing-command-on-a-single-node "Direct link to heading") --------------------------------------------------------------------------------------------------- As we all know command can be executed on single node similar to the examples above To execute ssh command on a single node, use ``, `` or `` instead of `` as an argument. For example: ```bash avalanche node ssh i-0225fc39626b1edd3 [or] avalanche node ssh NodeID-9wdKQ3KJU3GqvgFTc4CUYvmefEFe8t6ka [or] avalanche node ssh 54.159.59.123 ``` In this case `--parallel=true` flag will be ignored Opening SSH Shell for ``[​](#opening-ssh-shell-for-nodeid "Direct link to heading") ------------------------------------------------------------------------------------------- If no command is provided, Avalanche-CLI will open an interactive session for the specified node. For example: ```bash avalanche node ssh i-0225fc39626b1edd3 [or] avalanche node ssh NodeID-9wdKQ3KJU3GqvgFTc4CUYvmefEFe8t6ka [or] avalanche node ssh 54.159.59.123 ``` Please use `exit` shell command or Ctrl+D to end this session. # Run Load Test (/docs/tooling/avalanche-cli/create-avalanche-nodes/run-loadtest) --- title: Run Load Test description: This page demonstrates how to run load test on an Avalanche L1 deployed on a cluster of cloud-based validators using Avalanche-CLI. --- ## Prerequisites Before we begin, you will need to have: - Created an AWS account and have an updated AWS `credentials` file in home directory with [default] profile or set up your GCP account according to [here](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-gcp) - Created a cluster of cloud servers with monitoring enabled - Deployed an Avalanche L1 into the cluster - Added the cloud servers as validator nodes in the Avalanche L1 ## Run Load Test When the load test command is run, a new cloud server will be created to run the load test. The created cloud server is referred by the name `` and you can use any name of your choice. To start load test, run: ```bash avalanche node loadtest start ``` Next, you will need to provide the load test Git repository URL, load test Git Branch, the command to build the load test binary and the command to run the load test binary. We will use an example of running load test on an Avalanche L1 running custom VM MorpheusVM built with [HyperSDK](https://github.com/ava-labs/hypersdk/tree/main/examples/morpheusvm). The following settings will be used: - Load Test Repo URL: `https://github.com/ava-labs/hypersdk/` - Load Test Branch: `vryx-poc` - Load Test Build Script: `cd /home/ubuntu/hypersdk/examples/morpheusvm; CGO_CFLAGS=\"-O -D__BLST_PORTABLE__\" go build -o ~/simulator ./cmd/morpheus-cli` - Load Test Run Script: `/home/ubuntu/simulator spam run ed25519 --accounts=10000000 --txs-per-second=100000 --min-capacity=15000 --step-size=1000 --s-zipf=1.0001 --v-zipf=2.7 --conns-per-host=10 --cluster-info=/home/ubuntu/clusterInfo.yaml --private-key=323b1d8f4eed5f0da9da93071b034f2dce9d2d22692c172f3cb252a64ddfafd01b057de320297c29ad0c1f589ea216869cf1938d88c9fbd70d6748323dbf2fa7` Once the command is run, you will be able to see the logs from the load test in the cluster's Grafana URL like the example below: ![Centralized Logs](/images/centralized-logs.png) ## Stop Load Test To stop the load test process on the load test instance `` and terminate the load test instance, run: ```bash avalanche node loadtest stop ``` # Run Validator on AWS (/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-aws) --- title: Run Validator on AWS description: This page demonstrates how to deploy Avalanche validators on AWS using just one Avalanche-CLI command. --- This page demonstrates how to deploy Avalanche validators on AWS using just one Avalanche-CLI command. Currently, only Fuji network and Devnets are supported. ALPHA WARNING: This command is currently in experimental mode. Proceed at your own risk. ## Prerequisites Before we begin, you will need to create an AWS account and have an AWS `credentials` file in home directory with \[default\] profile set. More info can be found [here](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html#file-format-creds) ## Create Validators To create Avalanche validators, run: ```bash avalanche node create ``` The created nodes will be part of cluster `clusterName` and all avalanche node commands applied to cluster `clusterName` will apply to all nodes in the cluster. Please note that running a validator on AWS will incur costs. Ava Labs is not responsible for the cost incurred from running an Avalanche validator on cloud services via Avalanche-CLI. Currently, we have set the following specs of the AWS cloud server to a fixed value, but we plan to enable customization in the near future: - OS Image: `Ubuntu 20.04 LTS (HVM), SSD Volume Type` - Storage: `1 TB` Instance type can be specified via `--node-type` parameter or via interactive menu. `c5.2xlarge` is the default(recommended) instance size. The command will ask which region you want to set up your cloud server in: ```bash Which AWS region do you want to set up your node in?: ▸ us-east-1 us-east-2 us-west-1 us-west-2 Choose custom region (list of regions available at https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html) ``` The command will next ask whether you want to set up monitoring for your nodes. ```bash Do you want to set up a separate instance to host monitoring? (This enables you to monitor all your set up instances in one dashboard): ▸ Yes No ``` Setting up monitoring on a separate AWS instance enables you to have a centralized Grafana logs and dashboard for all nodes in a cluster, as seen below: ![Centralized Logs](/images/centralized-logs.png) ![Main Dashboard](/images/run-validators1.png) The separate monitoring AWS instance will have similar specs to the default AWS cloud server, except for its storage, which will be set to 50 GB. Please note that setting up monitoring on a separate AWS instance will incur additional cost of setting up an additional AWS cloud server. The command will then ask which Avalanche Go version you would like to install in the cloud server. You can choose `default` (which will install the latest version) or you can enter the name of an Avalanche L1 created with CLI that you plan to be validated by this node (we will get the latest version that is compatible with the deployed Avalanche L1's RPC version). Once the command has successfully completed, Avalanche-CLI outputs all the created cloud server node IDs as well as the public IP that each node can be reached at. Avalanche-CLI also outputs the command that you can use to ssh into each cloud server node. Finally, if monitoring is set up, Avalanche-CLI will also output the Grafana link where the centralized dashboards and logs can be accessed. By the end of successful run of `create` command, Avalanche-CLI would have: - Installed Avalanche Go in cloud server - Installed Avalanche CLI in cloud server - Downloaded the `.pem` private key file to access the cloud server into your local `.ssh` directory. Back up this private key file as you will not be able to ssh into the cloud server node without it (unless `ssh-agent` is used). - Downloaded `staker.crt` and `staker.key` files to your local `.avalanche-cli` directory so that you can back up your node. More info about node backup can be found [here](/docs/nodes/maintain/backup-restore) - Started the process of bootstrapping your new Avalanche node to the Primary Network (for non-Devnet only). Please note that Avalance CLI can be configured to use `ssh-agent` for ssh communication. In this case public key will be read from there and cloud server will be accessible using it. Yubikey hardware can be also used to store private ssh key. Please use official Yubikey documentation, for example \[[https://developers.yubico.com/PGP/SSH\_authentication/](https://developers.yubico.com/PGP/SSH_authentication/)\] for more details. Check Bootstrap Status[​](#check-bootstrap-status "Direct link to heading") --------------------------------------------------------------------------- Ignore for Devnet Please note that you will have to wait until the nodes have finished bootstrapping before the nodes can be Primary Network or Avalanche L1 Validators. To check whether all the nodes in a cluster have finished bootstrapping, run `avalanche node status `. # Run Validator on GCP (/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-gcp) --- title: Run Validator on GCP description: This page demonstrates how to deploy Avalanche validators on GCP using just one Avalanche-CLI command. --- This page demonstrates how to deploy Avalanche validators on GCP using just one Avalanche-CLI command. Currently, only Fuji network and Devnets are supported. ALPHA WARNING: This command is currently in experimental mode. Proceed at your own risk. ## Prerequisites Before we begin, you will need to: - Create a GCP account [here](https://console.cloud.google.com/freetrial) and create a new project - Enable Compute Engine API [here](https://console.cloud.google.com/apis/api/compute.googleapis.com) - Download the key json for the automatically created service account as shown [here](https://cloud.google.com/iam/docs/keys-create-delete#creating) ## Create Validator To create Avalanche validators, run: ```bash avalanche node create ``` The created nodes will be part of cluster `clusterName` and all avalanche node commands applied to cluster `clusterName` will apply to all nodes in the cluster. Please note that running a validator on GCP will incur costs. Ava Labs is not responsible for the cost incurred from running an Avalanche validator on cloud services via Avalanche-CLI. Currently, we have set the following specs of the GCP cloud server to a fixed value, but we plan to enable customization in the near future: - OS Image: `Ubuntu 20.04 LTS` - Storage: `1 TB` Instance type can be specified via `--node-type` parameter or via interactive menu. `e2-standard-8` is default(recommended) instance size. The command will ask which region you want to set up your cloud server in: ```bash Which Google Region do you want to set up your node(s) in?: ▸ us-east1 us-central1 us-west1 Choose custom Google Region (list of Google Regions available at https://cloud.google.com/compute/docs/regions-zones/) ``` The command will next ask whether you want to set up monitoring for your nodes. ```bash Do you want to set up a separate instance to host monitoring? (This enables you to monitor all your set up instances in one dashboard): ▸ Yes No ``` Setting up monitoring on a separate GCP instance enables you to have a unified Grafana dashboard for all nodes in a cluster, as seen below: ![Centralized Logs](/images/centralized-logs.png) ![Main Dashboard](/images/gcp1.png) The separate monitoring GCP instance will have similar specs to the default GCP cloud server, except for its storage, which will be set to 50 GB. Please note that setting up monitoring on a separate GCP instance will incur additional cost of setting up an additional GCP cloud server. The command will then ask which Avalanche Go version you would like to install in the cloud server. You can choose `default` (which will install the latest version) or you can enter the name of an Avalanche L1 created with CLI that you plan to be validated by this node (we will get the latest version that is compatible with the deployed Avalanche L1's RPC version). Once the command has successfully completed, Avalanche-CLI outputs all the created cloud server node IDs as well as the public IP that each node can be reached at. Avalanche-CLI also outputs the command that you can use to ssh into each cloud server node. Finally, if monitoring is set up, Avalanche-CLI will also output the Grafana link where the centralized dashboards and logs can be accessed. By the end of successful run of `create` command, Avalanche-CLI would have: - Installed Avalanche Go in cloud server - Installed Avalanche CLI in cloud server - Downloaded the `.pem` private key file to access the cloud server into your local `.ssh` directory. Back up this private key file as you will not be able to ssh into the cloud server node without it (unless `ssh-agent` is used). - Downloaded `staker.crt` and `staker.key` files to your local `.avalanche-cli` directory so that you can back up your node. More info about node backup can be found [here](/docs/nodes/maintain/backup-restore) - Started the process of bootstrapping your new Avalanche node to the Primary Network Please note that Avalanche CLI can be configured to use `ssh-agent` for ssh access to cloud server. Yubikey hardware can be also used to store private ssh key. Please use official Yubikey documentation, for example \[[https://developers.yubico.com/PGP/SSH\_authentication/](https://developers.yubico.com/PGP/SSH_authentication/)\] for more details. Check Bootstrap Status[​](#check-bootstrap-status "Direct link to heading") --------------------------------------------------------------------------- Ignore for Devnet Please note that you will have to wait until the nodes have finished bootstrapping before the nodes can be Primary Network or Avalanche L1 Validators. To check whether all the nodes in a cluster have finished bootstrapping, run `avalanche node status `. # Setup a Devnet (/docs/tooling/avalanche-cli/create-avalanche-nodes/setup-devnet) --- title: Setup a Devnet description: This page demonstrates how to setup a Devnet of cloud-based validators using Avalanche-CLI, and deploy a VM into it. --- Devnets (Developer Networks) are isolated Avalanche networks deployed on the cloud. Similar to local networks in terms of configuration and usage but installed on remote nodes. Think of DevNets as being an intermediate step in the developer testing process after local network and before Fuji network. ALPHA WARNING: This command is currently in experimental mode. Proceed at your own risk. ## Prerequisites Before we begin, you will need to create an AWS account and have an updated AWS `credentials` file in home directory with [default] profile or set up your GCP account according to [here](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-gcp). Note: the tutorial uses AWS hosts, but Devnets can also be created and operated in other supported cloud providers, such as GCP. ## Setting up a Devnet Setting up a Devnet consists of: - Creating a cluster of cloud servers - Deploying an Avalanche L1 into the cluster - Adding the cloud servers as validator nodes in the Avalanche L1 To execute all steps above in one command, run: ```bash avalanche node devnet wiz ``` Command line flags can be used instead of interacting with the prompts. The complete command line flags for `devnet wiz` command can be found [here](/docs/tooling/cli-commands#node-devnet-wiz). Let's go through several examples with the full command (with flags) provided. ### Create a Devnet and Deploy Subnet-EVM Based Avalanche L1 into the Devnet For example, to spin up a Devnet with 5 validator nodes and 1 API node in 5 regions each (us-west-2,us-east-1,ap-south-1,ap-northeast-1,eu-west-1) in AWS with each node having spec of c7g.8xlarge AWS EC2 instance type and io2 volume type, with Avalanche L1 `` deployed into the Devnet, we will run : ```bash avalanche node devnet wiz --authorize-access --aws --num-apis 1,1,1,1,1 --num-validators 5,5,5,5,5 --region us-west-2,us-east-1,ap-south-1,ap-northeast-1,eu-west-1 --default-validator-params --node-type c7g.8xlarge --aws-volume-type=io2 Creating the devnet ... Waiting for node(s) in cluster to be healthy... ... Nodes healthy after 33 seconds Deploying the subnet ... Setting the nodes as subnet trackers ... Waiting for node(s) in cluster to be healthy... Nodes healthy after 33 seconds ... Waiting for node(s) in cluster to be syncing subnet ... Nodes Syncing after 5 seconds Adding nodes as subnet validators ... Waiting for node(s) in cluster to be validating subnet ... Nodes Validating after 23 seconds Devnet has been created and is validating subnet ! ``` ### Create a Devnet and Deploy a Custom VM based Avalanche L1 into the Devnet For this example, we will be using the custom VM [MorpheusVM](https://github.com/ava-labs/hypersdk/tree/main/examples/morpheusvm) built with [HyperSDK](https://github.com/ava-labs/hypersdk). The following settings will be used: - `` `https://github.com/ava-labs/hypersdk/` - `` `vryx-poc` - `` `examples/morpheusvm/scripts/build.sh` - `` [Genesis File](/docs/tooling/avalanche-cli/create-avalanche-nodes/deploy-custom-vm#genesis-file) - `` [Blockchain Config](/docs/tooling/avalanche-cli/create-avalanche-nodes/deploy-custom-vm#blockchain-config) - `` [Avalanche L1 Config](/docs/tooling/avalanche-cli/create-avalanche-nodes/deploy-custom-vm#avalanche-l1-config) - `` [AvalancheGo Config](/docs/tooling/avalanche-cli/create-avalanche-nodes/deploy-custom-vm#avalanchego-flags) To spin up a Devnet with 5 validator nodes and 1 API node in 5 regions each (us-west-2,us-east-1,ap-south-1,ap-northeast-1,eu-west-1) in AWS with each node having spec of c7g.8xlarge AWS EC2 instance type and io2 volume type, with the Custom VM based Avalanche L1 `` deployed into the Devnet, we will run : ```bash avalanche node devnet wiz --custom-subnet \ --subnet-genesis --custom-vm-repo-url \ --custom-vm-branch --custom-vm-build-script \ --chain-config --subnet-config \ --node-config --authorize-access --aws --num-apis 1,1,1,1,1 \ --num-validators 5,5,5,5,5 --region us-west-2,us-east-1,ap-south-1,ap-northeast-1,eu-west-1 \ --default-validator-params --node-type default Creating the subnet ... Creating the devnet ... Waiting for node(s) in cluster to be healthy... ... Nodes healthy after 33 seconds Deploying the subnet ... Setting the nodes as subnet trackers ... Waiting for node(s) in cluster to be healthy... Nodes healthy after 33 seconds ... Waiting for node(s) in cluster to be syncing subnet ... Nodes Syncing after 5 seconds Adding nodes as subnet validators ... Waiting for node(s) in cluster to be validating subnet ... Nodes Validating after 23 seconds Devnet has been created and is validating subnet ! ``` # Terminate All Nodes (/docs/tooling/avalanche-cli/create-avalanche-nodes/stop-node) --- title: Terminate All Nodes description: This page provides instructions for terminating cloud server nodes created by Avalanche-CLI. --- ALPHA WARNING: This command is currently in experimental mode. Proceed at your own risk. Terminating All Nodes[​](#terminating-all-nodes "Direct link to heading") ------------------------------------------------------------------------- To terminate all nodes in a cluster, run: ```bash avalanche node destroy ``` ALPHA WARNING: This command will delete all files associated with the cloud servers in the cluster. This includes the downloaded `staker.crt` and `staker.key` files in your local `.avalanche-cli` directory (which are used to back up your node). More info about node backup can be found [here](/docs/nodes/maintain/backup-restore). Once completed, the instance set up on AWS / GCP would have been terminated and the Static Public IP associated with it would have been released. # Validate the Primary Network (/docs/tooling/avalanche-cli/create-avalanche-nodes/validate-primary-network) --- title: Validate the Primary Network description: This page demonstrates how to configure nodes to validate the Primary Network. Validation via Avalanche-CLI is currently only supported on Fuji. --- ALPHA WARNING: This command is currently in experimental mode. Proceed at your own risk. ## Prerequisites Before we begin, you will need to have: - Created a Cloud Server node as described for [AWS](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-aws) or [GCP](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-gcp) - A node bootstrapped to the Primary Network (run `avalanche node status `to check bootstrap status as described [here](/docs/tooling/avalanche-cli/create-avalanche-nodes/run-validators-aws#check-bootstrap-status) - Stored key / Ledger with AVAX to pay for gas fess associated with adding node as Primary Network. Instructions on how to fund stored key on Fuji can be found [here](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet#funding-the-key). Be a Primary Network Validator[​](#be-a-primary-network-validator "Direct link to heading") ------------------------------------------------------------------------------------------- Once all nodes in a cluster are bootstrapped to Primary Network, we can now have the nodes be Primary Network Validators. To have all nodes in cluster `clusterName` be Primary Network Validators, run: ```bash avalanche node validate primary ``` The nodes will start validating the Primary Network 20 seconds after the command is run. The wizard will ask us how we want to pay for the transaction fee. Choose `Use stored key` for Fuji: ```bash Which key source should be used to pay transaction fees?: ▸ Use stored key Use ledger ``` Once you have selected the key to pay with, choose how many AVAX you would like to stake in the validator. Default is the minimum amount of AVAX that can be staked in a Fuji Network Validator. More info regarding minimum staking amount in different networks can be found [here](/docs/primary-network/validate/how-to-stake#fuji-testnet). ```bash What stake weight would you like to assign to the validator?: ▸ Default (1.00 AVAX) Custom ``` Next, choose how long the node will be validating for: ```bash How long should your validator validate for?: ▸ Minimum staking duration on primary network Custom ``` Once all the inputs are completed you will see transaction IDs indicating that all the nodes in the cluster will be Primary Network Validators once the start time has elapsed. # On Local Network (/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-locally) --- title: On Local Network description: This guide shows you how to deploy an Avalanche L1 to a local Avalanche network. --- This how-to guide focuses on taking an already created Avalanche L1 configuration and deploying it to a local Avalanche network. ## Prerequisites - [Avalanche-CLI installed](/docs/tooling/avalanche-cli) - You have [created an Avalanche L1 configuration](/docs/tooling/avalanche-cli#create-your-avalanche-l1-configuration) Deploying Avalanche L1s Locally[​](#deploying-avalanche-l1s-locally "Direct link to heading") --------------------------------------------------------------------------------- In the following commands, make sure to substitute the name of your Avalanche L1 configuration for ``. To deploy your Avalanche L1, run: ```bash avalanche blockchain deploy ``` and select `Local Network` to deploy on. Alternatively, you can bypass this prompt by providing the `--local` flag. For example: ```bash avalanche blockchain deploy --local ``` The command may take a couple minutes to run. Note: If you run `bash` on your shell and are running Avalanche-CLI on ARM64 on Mac, you will require Rosetta 2 to be able to deploy Avalanche L1s locally. You can download Rosetta 2 using `softwareupdate --install-rosetta` . ### Results[​](#results "Direct link to heading") If all works as expected, the command output should look something like this: ```bash > avalanche blockchain deploy myblockchain ✔ Local Network Deploying [myblockchain] to Local Network AvalancheGo path: /Users/felipe.madero/.avalanche-cli/bin/avalanchego/avalanchego-v1.13.0/avalanchego Booting Network. Wait until healthy... Node logs directory: /Users/felipe.madero/.avalanche-cli/runs/network_20250410_104205//logs Network ready to use. Using [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] to be set as a change owner for leftover AVAX AvalancheGo path: /Users/felipe.madero/.avalanche-cli/bin/avalanchego/avalanchego-v1.13.0/avalanchego ✓ Local cluster myblockchain-local-node-local-network not found. Creating... Starting local avalanchego node using root: /Users/felipe.madero/.avalanche-cli/local/myblockchain-local-node-local-network ... ✓ Booting Network. Wait until healthy... ✓ Avalanchego started and ready to use from /Users/felipe.madero/.avalanche-cli/local/myblockchain-local-node-local-network Node logs directory: /Users/felipe.madero/.avalanche-cli/local/myblockchain-local-node-local-network//logs Network ready to use. URI: http://127.0.0.1:60172 NodeID: NodeID-NuQc8BQ8mV9TVksgMtpyc57VnWzU2J6aN Your blockchain control keys: [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] Your blockchain auth keys for chain creation: [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] CreateSubnetTx fee: 0.000010278 AVAX Blockchain has been created with ID: 2W9boARgCWL25z6pMFNtkCfNA5v28VGg9PmBgUJfuKndEdhrvw Now creating blockchain... CreateChainTx fee: 0.000129564 AVAX +--------------------------------------------------------------------+ | DEPLOYMENT RESULTS | +---------------+----------------------------------------------------+ | Chain Name | myblockchain | +---------------+----------------------------------------------------+ | Subnet ID | 2W9boARgCWL25z6pMFNtkCfNA5v28VGg9PmBgUJfuKndEdhrvw | +---------------+----------------------------------------------------+ | VM ID | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | +---------------+----------------------------------------------------+ | Blockchain ID | Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y | +---------------+ | | P-Chain TXID | | +---------------+----------------------------------------------------+ Now calling ConvertSubnetToL1Tx... ConvertSubnetToL1Tx fee: 0.000036992 AVAX ConvertSubnetToL1Tx ID: 2d2EE7AorEhfKLBtnDGnAtcDYMGfPbWnHYDpNDm3SopYg6VtpV Waiting for the Subnet to be converted into a sovereign L1 ... 100% [===============] Validator Manager Protocol: ACP99 Restarting node NodeID-NuQc8BQ8mV9TVksgMtpyc57VnWzU2J6aN to track newly deployed subnet/s Waiting for blockchain Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y to be bootstrapped ✓ Local Network successfully tracking myblockchain ✓ Checking if node is healthy... ✓ Node is healthy after 0 seconds Initializing Proof of Authority Validator Manager contract on blockchain myblockchain ... ✓ Proof of Authority Validator Manager contract successfully initialized on blockchain myblockchain Your L1 is ready for on-chain interactions. RPC Endpoint: http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc ICM Messenger successfully deployed to myblockchain (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) ICM Registry successfully deployed to myblockchain (0xEc7018552DC7E197Af85f157515f5976b1A15B12) ICM Messenger successfully deployed to c-chain (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) ICM Registry successfully deployed to c-chain (0x17aB05351fC94a1a67Bf3f56DdbB941aE6c63E25) ✓ ICM is successfully deployed Generating relayer config file at /Users/felipe.madero/.avalanche-cli/runs/network_20250410_104205/icm-relayer-config.json Relayer version icm-relayer-v1.6.2 Executing Relayer ✓ Relayer is successfully deployed +--------------------------------------------------------------------------------------------------------------------------------+ | MYBLOCKCHAIN | +---------------+----------------------------------------------------------------------------------------------------------------+ | Name | myblockchain | +---------------+----------------------------------------------------------------------------------------------------------------+ | VM ID | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | +---------------+----------------------------------------------------------------------------------------------------------------+ | VM Version | v0.7.3 | +---------------+----------------------------------------------------------------------------------------------------------------+ | Validation | Proof Of Authority | +---------------+--------------------------+-------------------------------------------------------------------------------------+ | Local Network | ChainID | 888 | | +--------------------------+-------------------------------------------------------------------------------------+ | | SubnetID | 2W9boARgCWL25z6pMFNtkCfNA5v28VGg9PmBgUJfuKndEdhrvw | | +--------------------------+-------------------------------------------------------------------------------------+ | | Owners (Threhold=1) | P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p | | +--------------------------+-------------------------------------------------------------------------------------+ | | BlockchainID (CB58) | Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y | | +--------------------------+-------------------------------------------------------------------------------------+ | | BlockchainID (HEX) | 0x48644613a5ef255fa171bf4773df668b57ea0ea9593df8927a6d9f32376a9c6f | | +--------------------------+-------------------------------------------------------------------------------------+ | | RPC Endpoint | http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc | +---------------+--------------------------+-------------------------------------------------------------------------------------+ +------------------------------------------------------------------------------------+ | ICM | +---------------+-----------------------+--------------------------------------------+ | Local Network | ICM Messenger Address | 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf | | +-----------------------+--------------------------------------------+ | | ICM Registry Address | 0xEc7018552DC7E197Af85f157515f5976b1A15B12 | +---------------+-----------------------+--------------------------------------------+ +--------------------------+ | TOKEN | +--------------+-----------+ | Token Name | TST Token | +--------------+-----------+ | Token Symbol | TST | +--------------+-----------+ +---------------------------------------------------------------------------------------------------------------------------------------+ | INITIAL TOKEN ALLOCATION | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ | DESCRIPTION | ADDRESS AND PRIVATE KEY | AMOUNT (TST) | AMOUNT (WEI) | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ | Main funded account | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | 1000000 | 1000000000000000000000000 | | ewoq | 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 | | | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ | Used by ICM | 0xf34408C05e3B339B1c89d15163d4B9D96845597A | 600 | 600000000000000000000 | | cli-teleporter-deployer | 30d57c7b6e7e393e2e4ce8166768b497cc37930361a15b1c647d6e665d88afff | | | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ +----------------------------------------------------------------------------------------------------------------------------------+ | SMART CONTRACTS | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | DESCRIPTION | ADDRESS | DEPLOYER | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | Validator Messages Lib | 0x9C00629cE712B0255b17A4a657171Acd15720B8C | | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | Proxy Admin | 0xC0fFEE1234567890aBCdeF1234567890abcDef34 | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | ACP99 Compatible PoA Validator Manager | 0x0C0DEbA5E0000000000000000000000000000000 | | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | Transparent Proxy | 0x0Feedc0de0000000000000000000000000000000 | | +----------------------------------------+--------------------------------------------+--------------------------------------------+ +----------------------------------------------------------------------+ | INITIAL PRECOMPILE CONFIGS | +------------+-----------------+-------------------+-------------------+ | PRECOMPILE | ADMIN ADDRESSES | MANAGER ADDRESSES | ENABLED ADDRESSES | +------------+-----------------+-------------------+-------------------+ | Warp | n/a | n/a | n/a | +------------+-----------------+-------------------+-------------------+ +-------------------------------------------------------------------------------------------------+ | MYBLOCKCHAIN RPC URLS | +-----------+-------------------------------------------------------------------------------------+ | Localhost | http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc | +-----------+-------------------------------------------------------------------------------------+ +------------------------------------------------------------------+ | PRIMARY NODES | +------------------------------------------+-----------------------+ | NODE ID | LOCALHOST ENDPOINT | +------------------------------------------+-----------------------+ | NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg | http://127.0.0.1:9650 | +------------------------------------------+-----------------------+ | NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ | http://127.0.0.1:9652 | +------------------------------------------+-----------------------+ +----------------------------------------------------------------------------------+ | L1 NODES | +------------------------------------------+------------------------+--------------+ | NODE ID | LOCALHOST ENDPOINT | L1 | +------------------------------------------+------------------------+--------------+ | NodeID-NuQc8BQ8mV9TVksgMtpyc57VnWzU2J6aN | http://127.0.0.1:60172 | myblockchain | +------------------------------------------+------------------------+--------------+ +-------------------------------------------------------------------------------------------------------+ | WALLET CONNECTION | +-----------------+-------------------------------------------------------------------------------------+ | Network RPC URL | http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc | +-----------------+-------------------------------------------------------------------------------------+ | Network Name | myblockchain | +-----------------+-------------------------------------------------------------------------------------+ | Chain ID | 888 | +-----------------+-------------------------------------------------------------------------------------+ | Token Symbol | TST | +-----------------+-------------------------------------------------------------------------------------+ | Token Name | TST Token | +-----------------+-------------------------------------------------------------------------------------+ ✓ L1 is successfully deployed on Local Network ``` You can use the deployment details to connect to and interact with your Avalanche L1. Deploying Avalanche L1s Locally[​](#deploying-avalanche-l1s-locally) --------------------------------------------------------------------------------- To deploy your Avalanche L1, run: ```bash avalanche blockchain deploy myblockchain ``` Make sure to substitute the name of your Avalanche L1 if you used a different one than `myblockchain`. ```bash ? Choose a network for the operation: ▸ Local Network Devnet Etna Devnet Fuji Testnet Mainnet ``` Next, select `Local Network`. This command boots a three node Avalanche network on your machine: - Two nodes to act as primary validators for the local network, that will validate the local P and C chains (unrelated to testnet/mainnet).. - One node to act as sovereign validator for the new L1 that is deployed into the local network. The command needs to download the latest versions of AvalancheGo and Subnet-EVM. It may take a couple minutes to run. Note: If you run `bash` on your shell and are running Avalanche-CLI on ARM64 on Mac, you will require Rosetta 2 to be able to deploy Avalanche L1s locally. You can download Rosetta 2 using `softwareupdate --install-rosetta` . If all works as expected, the command output should look something like this: ```bash avalanche blockchain deploy myblockchain # output ✔ Local Network Deploying [myblockchain] to Local Network AvalancheGo path: /Users/felipe.madero/.avalanche-cli/bin/avalanchego/avalanchego-v1.13.0/avalanchego Booting Network. Wait until healthy... Node logs directory: /Users/felipe.madero/.avalanche-cli/runs/network_20250410_104205//logs Network ready to use. Using [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] to be set as a change owner for leftover AVAX AvalancheGo path: /Users/felipe.madero/.avalanche-cli/bin/avalanchego/avalanchego-v1.13.0/avalanchego ✓ Local cluster myblockchain-local-node-local-network not found. Creating... Starting local avalanchego node using root: /Users/felipe.madero/.avalanche-cli/local/myblockchain-local-node-local-network ... ✓ Booting Network. Wait until healthy... ✓ Avalanchego started and ready to use from /Users/felipe.madero/.avalanche-cli/local/myblockchain-local-node-local-network Node logs directory: /Users/felipe.madero/.avalanche-cli/local/myblockchain-local-node-local-network//logs Network ready to use. URI: http://127.0.0.1:60172 NodeID: NodeID-NuQc8BQ8mV9TVksgMtpyc57VnWzU2J6aN Your blockchain control keys: [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] Your blockchain auth keys for chain creation: [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] CreateSubnetTx fee: 0.000010278 AVAX Blockchain has been created with ID: 2W9boARgCWL25z6pMFNtkCfNA5v28VGg9PmBgUJfuKndEdhrvw Now creating blockchain... CreateChainTx fee: 0.000129564 AVAX +--------------------------------------------------------------------+ | DEPLOYMENT RESULTS | +---------------+----------------------------------------------------+ | Chain Name | myblockchain | +---------------+----------------------------------------------------+ | Subnet ID | 2W9boARgCWL25z6pMFNtkCfNA5v28VGg9PmBgUJfuKndEdhrvw | +---------------+----------------------------------------------------+ | VM ID | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | +---------------+----------------------------------------------------+ | Blockchain ID | Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y | +---------------+ | | P-Chain TXID | | +---------------+----------------------------------------------------+ Now calling ConvertSubnetToL1Tx... ConvertSubnetToL1Tx fee: 0.000036992 AVAX ConvertSubnetToL1Tx ID: 2d2EE7AorEhfKLBtnDGnAtcDYMGfPbWnHYDpNDm3SopYg6VtpV Waiting for the Subnet to be converted into a sovereign L1 ... 100% [===============] Validator Manager Protocol: ACP99 Restarting node NodeID-NuQc8BQ8mV9TVksgMtpyc57VnWzU2J6aN to track newly deployed subnet/s Waiting for blockchain Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y to be bootstrapped ✓ Local Network successfully tracking myblockchain ✓ Checking if node is healthy... ✓ Node is healthy after 0 seconds Initializing Proof of Authority Validator Manager contract on blockchain myblockchain ... ✓ Proof of Authority Validator Manager contract successfully initialized on blockchain myblockchain Your L1 is ready for on-chain interactions. RPC Endpoint: http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc ICM Messenger successfully deployed to myblockchain (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) ICM Registry successfully deployed to myblockchain (0xEc7018552DC7E197Af85f157515f5976b1A15B12) ICM Messenger successfully deployed to c-chain (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) ICM Registry successfully deployed to c-chain (0x17aB05351fC94a1a67Bf3f56DdbB941aE6c63E25) ✓ ICM is successfully deployed Generating relayer config file at /Users/felipe.madero/.avalanche-cli/runs/network_20250410_104205/icm-relayer-config.json Relayer version icm-relayer-v1.6.2 Executing Relayer ✓ Relayer is successfully deployed +--------------------------------------------------------------------------------------------------------------------------------+ | MYBLOCKCHAIN | +---------------+----------------------------------------------------------------------------------------------------------------+ | Name | myblockchain | +---------------+----------------------------------------------------------------------------------------------------------------+ | VM ID | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | +---------------+----------------------------------------------------------------------------------------------------------------+ | VM Version | v0.7.3 | +---------------+----------------------------------------------------------------------------------------------------------------+ | Validation | Proof Of Authority | +---------------+--------------------------+-------------------------------------------------------------------------------------+ | Local Network | ChainID | 888 | | +--------------------------+-------------------------------------------------------------------------------------+ | | SubnetID | 2W9boARgCWL25z6pMFNtkCfNA5v28VGg9PmBgUJfuKndEdhrvw | | +--------------------------+-------------------------------------------------------------------------------------+ | | Owners (Threhold=1) | P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p | | +--------------------------+-------------------------------------------------------------------------------------+ | | BlockchainID (CB58) | Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y | | +--------------------------+-------------------------------------------------------------------------------------+ | | BlockchainID (HEX) | 0x48644613a5ef255fa171bf4773df668b57ea0ea9593df8927a6d9f32376a9c6f | | +--------------------------+-------------------------------------------------------------------------------------+ | | RPC Endpoint | http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc | +---------------+--------------------------+-------------------------------------------------------------------------------------+ +------------------------------------------------------------------------------------+ | ICM | +---------------+-----------------------+--------------------------------------------+ | Local Network | ICM Messenger Address | 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf | | +-----------------------+--------------------------------------------+ | | ICM Registry Address | 0xEc7018552DC7E197Af85f157515f5976b1A15B12 | +---------------+-----------------------+--------------------------------------------+ +--------------------------+ | TOKEN | +--------------+-----------+ | Token Name | TST Token | +--------------+-----------+ | Token Symbol | TST | +--------------+-----------+ +---------------------------------------------------------------------------------------------------------------------------------------+ | INITIAL TOKEN ALLOCATION | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ | DESCRIPTION | ADDRESS AND PRIVATE KEY | AMOUNT (TST) | AMOUNT (WEI) | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ | Main funded account | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | 1000000 | 1000000000000000000000000 | | ewoq | 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 | | | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ | Used by ICM | 0xf34408C05e3B339B1c89d15163d4B9D96845597A | 600 | 600000000000000000000 | | cli-teleporter-deployer | 30d57c7b6e7e393e2e4ce8166768b497cc37930361a15b1c647d6e665d88afff | | | +-------------------------+------------------------------------------------------------------+--------------+---------------------------+ +----------------------------------------------------------------------------------------------------------------------------------+ | SMART CONTRACTS | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | DESCRIPTION | ADDRESS | DEPLOYER | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | Validator Messages Lib | 0x9C00629cE712B0255b17A4a657171Acd15720B8C | | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | Proxy Admin | 0xC0fFEE1234567890aBCdeF1234567890abcDef34 | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | ACP99 Compatible PoA Validator Manager | 0x0C0DEbA5E0000000000000000000000000000000 | | +----------------------------------------+--------------------------------------------+--------------------------------------------+ | Transparent Proxy | 0x0Feedc0de0000000000000000000000000000000 | | +----------------------------------------+--------------------------------------------+--------------------------------------------+ +----------------------------------------------------------------------+ | INITIAL PRECOMPILE CONFIGS | +------------+-----------------+-------------------+-------------------+ | PRECOMPILE | ADMIN ADDRESSES | MANAGER ADDRESSES | ENABLED ADDRESSES | +------------+-----------------+-------------------+-------------------+ | Warp | n/a | n/a | n/a | +------------+-----------------+-------------------+-------------------+ +-------------------------------------------------------------------------------------------------+ | MYBLOCKCHAIN RPC URLS | +-----------+-------------------------------------------------------------------------------------+ | Localhost | http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc | +-----------+-------------------------------------------------------------------------------------+ +------------------------------------------------------------------+ | PRIMARY NODES | +------------------------------------------+-----------------------+ | NODE ID | LOCALHOST ENDPOINT | +------------------------------------------+-----------------------+ | NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg | http://127.0.0.1:9650 | +------------------------------------------+-----------------------+ | NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ | http://127.0.0.1:9652 | +------------------------------------------+-----------------------+ +----------------------------------------------------------------------------------+ | L1 NODES | +------------------------------------------+------------------------+--------------+ | NODE ID | LOCALHOST ENDPOINT | L1 | +------------------------------------------+------------------------+--------------+ | NodeID-NuQc8BQ8mV9TVksgMtpyc57VnWzU2J6aN | http://127.0.0.1:60172 | myblockchain | +------------------------------------------+------------------------+--------------+ +-------------------------------------------------------------------------------------------------------+ | WALLET CONNECTION | +-----------------+-------------------------------------------------------------------------------------+ | Network RPC URL | http://127.0.0.1:60172/ext/bc/Yt9d8RRW9JcoqfvyefqJJMX14HawtBc28J9CQspQKPkdonp1y/rpc | +-----------------+-------------------------------------------------------------------------------------+ | Network Name | myblockchain | +-----------------+-------------------------------------------------------------------------------------+ | Chain ID | 888 | +-----------------+-------------------------------------------------------------------------------------+ | Token Symbol | TST | +-----------------+-------------------------------------------------------------------------------------+ | Token Name | TST Token | +-----------------+-------------------------------------------------------------------------------------+ ✓ L1 is successfully deployed on Local Network ``` To manage the newly deployed local Avalanche network, see [the `avalanche network` command tree](/docs/tooling/cli-commands#avalanche-network). You can use the deployment details to connect to and interact with your Avalanche L1. Now it's time to interact with it. ## Interacting with Your Avalanche L1 You can use the value provided by `Browser Extension connection details` to connect to your Avalanche L1 with Core, MetaMask, or any other wallet. To allow API calls from other machines, use `--http-host=0.0.0.0` in the config. ```bash Browser Extension connection details (any node URL from above works): RPC URL: http://127.0.0.1:9650/ext/bc/2BK8CKA4Vfvi69TBTc5GW94JQ9nPiL8xPpPNeeckb9UFSPYedD/rpc Funded address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC with 1000000 (10^18) - private key: 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 Network name: myblockchain Chain ID: 888 Currency Symbol: TST ``` This tutorial uses Core. ### Importing the Test Private Key[​](#importing-the-test-private-key) This address derives from a well-known private key. Anyone can steal funds sent to this address. Only use it on development networks that only you have access to. If you send production funds to this address, attackers may steal them instantly. First, you need to import your airdrop private key into Core. In the Accounts screen, select the `Imported` tab. Click on `Import private key`. ![](/images/deploy-subnet1.png) Here, enter the private key. Import the well-known private key `0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027`. ![](/images/deploy-subnet2.png) Next, rename the Core account to prevent confusion. On the `Imported` tab, click on the pen icon next to your account. Rename the account `DO NOT USE -- Public test key` to prevent confusion with any personal wallets. ![Rename Account](/images/deploy-subnet3.png) ![Rename Account](/images/deploy-subnet4.png) ### Connect to the Avalanche L1[​](#connect-to-the-avalanche-l1) Next, you need to add your Avalanche L1 to Core's networks. In the Core Extension click, `See All Networks` and then select the `+` icon in the top right. ![Add network](/images/deploy-subnet5.png) Enter your Avalanche L1's details, found in the output of your `avalanche blockchain deploy` [command](#deploying-avalanche-l1s-locally), into the form and click `Save`. ![Add network 2](/images/deploy-subnet6.png) If all worked as expected, your balance should read 1 million tokens. Your Avalanche L1 is ready for action. You might want to try to [Deploy a Smart Contract on Your Subnet-EVM Using Remix and Core](/docs/avalanche-l1s/add-utility/deploy-smart-contract). ![Avalanche L1 in Core](/images/deploy-subnet7.png) ## Deploying Multiple Avalanche L1s[​](#deploying-multiple-avalanche-l1s "Direct link to heading") You may deploy multiple Avalanche L1s concurrently, but you can't deploy the same Avalanche L1 multiple times without resetting all deployed Avalanche L1 state. ## Redeploying the Avalanche L1 To redeploy the Avalanche L1, you first need to wipe the Avalanche L1 state. This permanently deletes all data from all locally deployed Avalanche L1s. To do so, run ```bash avalanche network clean ``` You are now free to redeploy your Avalanche L1 with ```bash avalanche blockchain deploy --local ``` ## Stopping the Local Network To gracefully stop a running local network while preserving state, run: ```bash avalanche network stop # output Network stopped successfully. ``` When restarted, all of your deployed Avalanche L1s resume where they left off. ### Resuming the Local Network To resume a stopped network, run: ```bash avalanche network start # output Starting previously deployed and stopped snapshot Booting Network. Wait until healthy... ............... Network ready to use. Local network node endpoints: +-------+----------+------------------------------------------------------------------------------------+ | NODE | VM | URL | +-------+----------+------------------------------------------------------------------------------------+ | node5 | myblockchain | http://127.0.0.1:9658/ext/bc/SPqou41AALqxDquEycNYuTJmRvZYbfoV9DYApDJVXKXuwVFPz/rpc | +-------+----------+------------------------------------------------------------------------------------+ | node1 | myblockchain | http://127.0.0.1:9650/ext/bc/SPqou41AALqxDquEycNYuTJmRvZYbfoV9DYApDJVXKXuwVFPz/rpc | +-------+----------+------------------------------------------------------------------------------------+ | node2 | myblockchain | http://127.0.0.1:9652/ext/bc/SPqou41AALqxDquEycNYuTJmRvZYbfoV9DYApDJVXKXuwVFPz/rpc | +-------+----------+------------------------------------------------------------------------------------+ | node3 | myblockchain | http://127.0.0.1:9654/ext/bc/SPqou41AALqxDquEycNYuTJmRvZYbfoV9DYApDJVXKXuwVFPz/rpc | +-------+----------+------------------------------------------------------------------------------------+ | node4 | myblockchain | http://127.0.0.1:9656/ext/bc/SPqou41AALqxDquEycNYuTJmRvZYbfoV9DYApDJVXKXuwVFPz/rpc | +-------+----------+------------------------------------------------------------------------------------+ ``` The network resumes with the same state it paused with. ## Next Steps After you feel comfortable with this deployment flow, try deploying smart contracts on your chain with [Remix](https://remix.ethereum.org/), [Hardhat](https://hardhat.org/), or [Foundry](https://github.com/foundry-rs/foundry). You can also experiment with customizing your Avalanche L1 by addingprecompiles or adjusting the airdrop. Once you've developed a stable Avalanche L1 you like, see [Create an EVM Avalanche L1 on Fuji Testnet](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1) to take your Avalanche L1 one step closer to production. ## FAQ **How is the Avalanche L1 ID (SubnetID) determined upon creation?** The Avalanche L1 ID (SubnetID) is the hash of the transaction that created the Avalanche L1. # On Fuji Testnet (/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet) --- title: On Fuji Testnet description: This tutorial shows how to deploy an Avalanche L1 on Fuji Testnet. --- This document describes how to use the new Avalanche-CLI to deploy an Avalanche L1 on `Fuji`. After trying out an Avalanche L1 on a local box by following [this tutorial](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-locally), next step is to try it out on `Fuji` Testnet. In this article, it's shown how to do the following on `Fuji` Testnet. - Create an Avalanche L1. - Deploy a virtual machine based on Subnet-EVM. - Join a node to the newly created Avalanche L1. - Add a node as a validator to the Avalanche L1. All IDs in this article are for illustration purposes. They can be different in your own run-through of this tutorial. ## Prerequisites - [`Avalanche-CLI`](https://github.com/ava-labs/avalanche-cli) installed Virtual Machine[​](#virtual-machine "Direct link to heading") ------------------------------------------------------------- Avalanche can run multiple blockchains. Each blockchain is an instance of a [Virtual Machine](/docs/primary-network/virtual-machines), much like an object in an object-oriented language is an instance of a class. That's, the VM defines the behavior of the blockchain. [Subnet-EVM](https://github.com/ava-labs/subnet-evm) is the VM that defines the Avalanche L1 Contract Chains. Subnet-EVM is a simplified version of [Avalanche C-Chain](https://github.com/ava-labs/avalanchego/tree/master/graft/coreth). This chain implements the Ethereum Virtual Machine and supports Solidity smart contracts as well as most other Ethereum client features. Avalanche-CLI[​](#avalanche-cli "Direct link to heading") --------------------------------------------------------- If not yet installed, install `Avalanche-CLI` following the tutorial at [Avalanche-CLI installation](/docs/tooling/avalanche-cli) ### Private Key[​](#private-key "Direct link to heading") All commands which issue a transaction require either a private key loaded into the tool, or a connected ledger device. This tutorial focuses on stored key usage and leave ledger operation details for the `Mainnet` deploy one, as `Mainnet` operations requires ledger usage, while for `Fuji` it's optional. `Avalanche-CLI` supports the following key operations: - create - delete - export - list You should only use the private key created for this tutorial for testing operations on `Fuji` or other testnets. Don't use this key on `Mainnet`. CLI is going to store the key on your file system. Whoever gets access to that key is going to have access to all funds secured by that private key. Run `create` if you don't have any private key available yet. You can create multiple named keys. Each command requiring a key is going to therefore require the appropriate key name you want to use. ```bash avalanche key create mytestkey ``` This is going to generate a new key named `mytestkey`. The command is going to then also print addresses associated with the key: ```bash Generating new key... Key created +-----------+-------------------------------+-------------------------------------------------+---------------+ | KEY NAME | CHAIN | ADDRESS | NETWORK | +-----------+-------------------------------+-------------------------------------------------+---------------+ | mytestkey | C-Chain (Ethereum hex format) | 0x86BB07a534ADF43786ECA5Dd34A97e3F96927e4F | All | + +-------------------------------+-------------------------------------------------+---------------+ | | P-Chain (Bech32 format) | P-custom1a3azftqvygc4tlqsdvd82wks2u7nx85rg7v8ta | Local Network | + + +-------------------------------------------------+---------------+ | | | P-fuji1a3azftqvygc4tlqsdvd82wks2u7nx85rhk6zqh | Fuji | +-----------+-------------------------------+-------------------------------------------------+---------------+ ``` You may use the C-Chain address (`0x86BB07a534ADF43786ECA5Dd34A97e3F96927e4F`) to fund your key: - **Recommended:** Create a [Builder Hub account](https://build.avax.network/login) and connect your wallet to receive testnet tokens automatically - **Alternative:** Use the [external faucet](https://core.app/tools/testnet-faucet/) The command also prints P-Chain addresses for both the default local network and `Fuji`. The latter (`P-fuji1a3azftqvygc4tlqsdvd82wks2u7nx85rhk6zqh`) is the one needed for this tutorial. The `delete` command of course deletes a private key: ```bash avalanche key delete mytestkey ``` Be careful though to always have a key available for commands involving transactions. The `export` command is going to **print your private key** in hex format to stdout. ```bash avalanche key export mytestkey 21940fbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb5f0b ``` This key is intentionally modified. You can also **import** a key by using the `--file` flag with a path argument and also providing a name to it: ```bash avalanche key create othertest --file /tmp/test.pk Loading user key... Key loaded ``` Finally, the `list` command is going to list all your keys in your system and their associated addresses (CLI stores the keys in a special directory on your file system, tampering with the directory is going to result in malfunction of the tool). ```bash avalanche key list +-----------+-------------------------------+-------------------------------------------------+---------------+ | KEY NAME | CHAIN | ADDRESS | NETWORK | +-----------+-------------------------------+-------------------------------------------------+---------------+ | othertest | C-Chain (Ethereum hex format) | 0x36c83263e33f9e87BB98D3fEb54a01E35a3Fa735 | All | + +-------------------------------+-------------------------------------------------+---------------+ | | P-Chain (Bech32 format) | P-custom1n5n4h99j3nx8hdrv50v8ll7aldm383nap6rh42 | Local Network | + + +-------------------------------------------------+---------------+ | | | P-fuji1n5n4h99j3nx8hdrv50v8ll7aldm383na7j4j7q | Fuji | +-----------+-------------------------------+-------------------------------------------------+---------------+ | mytestkey | C-Chain (Ethereum hex format) | 0x86BB07a534ADF43786ECA5Dd34A97e3F96927e4F | All | + +-------------------------------+-------------------------------------------------+---------------+ | | P-Chain (Bech32 format) | P-custom1a3azftqvygc4tlqsdvd82wks2u7nx85rg7v8ta | Local Network | + + +-------------------------------------------------+---------------+ | | | P-fuji1a3azftqvygc4tlqsdvd82wks2u7nx85rhk6zqh | Fuji | +-----------+-------------------------------+-------------------------------------------------+---------------+ ``` #### Funding the Key[​](#funding-the-key "Direct link to heading") Do these steps only to follow this tutorial for `Fuji` addresses. To access the wallet for `Mainnet`, the use of a ledger device is strongly recommended. 1. A newly created key has no funds on it. You have several options to fund it: - **Recommended:** Create a [Builder Hub account](https://build.avax.network/login) and connect your wallet to receive testnet tokens automatically on C-Chain and P-Chain - **Alternative:** Use the [external faucet](https://core.app/tools/testnet-faucet/) with your C-Chain address. If you have an AVAX balance on Mainnet, you can request tokens directly. Otherwise, request a faucet coupon on [Guild](https://guild.xyz/avalanche) or contact admins/mods on [Discord](https://discord.com/invite/RwXY7P6) 2. Export your key via the `avalanche key export` command. The output is your private key, which will help you [import](https://support.avax.network/en/articles/6821877-core-extension-how-can-i-import-an-account) your account into the Core extension. 3. Connect Core extension to [Core web](https://core.app/), and move the test funds from C-Chain to the P-Chain by clicking Stake, then Cross-Chain Transfer (find more details on [this tutorial](https://support.avax.network/en/articles/8133713-core-web-how-do-i-make-cross-chain-transfers-in-core-stake)). After following these 3 steps, your test key should now have a balance on the P-Chain on `Fuji` Testnet. Create an EVM Avalanche L1[​](#create-an-evm-avalanche-l1 "Direct link to heading") ----------------------------------------------------------------------- Creating an Avalanche L1 with `Avalanche-CLI` for `Fuji` works the same way as with a local network. In fact, the `create` commands only creates a specification of your Avalanche L1 on the local file system. Afterwards the Avalanche L1 needs to be _deployed_. This allows to reuse configs, by creating the config with the `create` command, then first deploying to a local network and successively to `Fuji` - and eventually to `Mainnet`. To create an EVM Avalanche L1, run the `blockchain create` command with a name of your choice: ```bash avalanche blockchain create testblockchain ``` This is going to start a series of prompts to customize your EVM Avalanche L1 to your needs. Most prompts have some validation to reduce issues due to invalid input. The first prompt asks for the type of the virtual machine (see [Virtual Machine](#virtual-machine)). ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose your VM: ▸ SubnetEVM Custom ``` As you want to create an EVM Avalanche L1, just accept the default `Subnet-EVM`. Choose either Proof of Authority (PoA) or Proof of Stake (PoS) as your consensus mechanism. ```bash ? Which validator management type would you like to use in your blockchain?: ▸ Proof Of Authority Proof Of Stake Explain the difference ``` For this tutorial, select `Proof of Authority (PoA)`. For more info, reference the [Validator Management Contracts](/docs/avalanche-l1s/validator-manager/contract). ```bash Which address do you want to enable as controller of ValidatorManager contract?: ▸ Get address from an existing stored key (created from avalanche key create or avalanche key import) Custom ``` This address will be able to add and remove validators from your Avalanche L1. You can either use an existing key or create a new one. In addition to being the PoA owner, this address will also be the owner of the `ProxyAdmin` contract of the Validator Manager's `TransparentUpgradeableProxy`. This address will be able to upgrade (PoA -> PoS) the Validator Manager implementation through updating the proxy. Next, CLI will ask for blockchain configuration values. Since we are deploying to Fuji, select `I want to use defaults for a production environment`. ```bash ? Do you want to use default values for the Blockchain configuration?: ▸ I want to use defaults for a test environment I want to use defaults for a production environment I don't want to use default values Explain the difference ``` The default values for production environment: - Use latest Subnet-EVM release - Allocate 1 million tokens to: 1. **a newly created key (production)**: name of this key will be in the format of `subnet_blockchainName_airdrop` 2. **ewoq address (test)**: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC - Supply of the native token will be hard-capped - Set gas fee config as low throughput (12 million gas per block) - Use constant gas prices - Disable further adjustments in transaction fee configuration - Transaction fees are burned - Enable interoperability with other blockchains - Allow any user to deploy smart contracts, send transactions, and interact with your blockchain. Next, CLI asks for the ChainID. You should provide your own ID. Check [chainlist.org](https://chainlist.org/) to see if the value you'd like is already in use. ```bash ✔ Subnet-EVM creating Avalanche L1 test blockchain Enter your Avalanche L1's ChainId. It can be any positive integer. ChainId: 3333 ``` Now, provide a symbol of your choice for the token of this EVM: ```bash Select a symbol for your Avalanche L1's native token Token symbol: TST ``` It's possible to end the process with Ctrl-C at any time. At this point, CLI creates the specification of the new Avalanche L1 on disk, but isn't deployed yet. Print the specification to disk by running the `describe` command: ```bash avalanche blockchain describe testblockchain ``` ```bash +------------------------------------------------------------------+ | TESTBLOCKCHAIN | +------------+-----------------------------------------------------+ | Name | testblockchain | +------------+-----------------------------------------------------+ | VM ID | tGBrM94jbkesczgqsL1UaxjrdxRQQobs3MZTNQ4GrfhzvpiE8 | +------------+-----------------------------------------------------+ | VM Version | v0.6.12 | +------------+-----------------------------------------------------+ | Validation | Proof Of Authority | +------------+-----------------------------------------------------+ +--------------------------+ | TOKEN | +--------------+-----------+ | Token Name | TST Token | +--------------+-----------+ | Token Symbol | TST | +--------------+-----------+ +-----------------------------------------------------------------------------------------------------------------------------------+ | INITIAL TOKEN ALLOCATION | +---------------------+------------------------------------------------------------------+--------------+---------------------------+ | DESCRIPTION | ADDRESS AND PRIVATE KEY | AMOUNT (TST) | AMOUNT (WEI) | +---------------------+------------------------------------------------------------------+--------------+---------------------------+ | Main funded account | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | 1000000 | 1000000000000000000000000 | | ewoq | 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 | | | +---------------------+------------------------------------------------------------------+--------------+---------------------------+ +-----------------------------------------------------------------------------------------------------------------+ | SMART CONTRACTS | +-----------------------+--------------------------------------------+--------------------------------------------+ | DESCRIPTION | ADDRESS | DEPLOYER | +-----------------------+--------------------------------------------+--------------------------------------------+ | Proxy Admin | 0xC0fFEE1234567890aBCdeF1234567890abcDef34 | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | +-----------------------+--------------------------------------------+--------------------------------------------+ | PoA Validator Manager | 0x0C0DEbA5E0000000000000000000000000000000 | | +-----------------------+--------------------------------------------+--------------------------------------------+ | Transparent Proxy | 0x0Feedc0de0000000000000000000000000000000 | | +-----------------------+--------------------------------------------+--------------------------------------------+ +----------------------------------------------------------------------+ | INITIAL PRECOMPILE CONFIGS | +------------+-----------------+-------------------+-------------------+ | PRECOMPILE | ADMIN ADDRESSES | MANAGER ADDRESSES | ENABLED ADDRESSES | +------------+-----------------+-------------------+-------------------+ | Warp | n/a | n/a | n/a | +------------+-----------------+-------------------+-------------------+ ``` Deploy the Avalanche L1[​](#deploy-the-avalanche-l1 "Direct link to heading") ----------------------------------------------------------------- To deploy the Avalanche L1, you will need some testnet AVAX on the P-chain. To deploy the new Avalanche L1, run: ```bash avalanche blockchain deploy testblockchain ``` This is going to start a new prompt series. ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to deploy on: ▸ Local Network Fuji Mainnet ``` This tutorial is about deploying to `Fuji`, so navigate with the arrow keys to `Fuji` and hit enter. The user is then asked to provide which private key to use for the deployment. Select a key to has P-Chain AVAX to pay for transaction fees. Also, this tutorial assumes that a node is up running, fully bootstrapped on `Fuji`, and runs from the **same** box. ```bash ✔ Fuji Deploying [testblockchain] to Fuji Use the arrow keys to navigate: ↓ ↑ → ← ? Which private key should be used to issue the transaction?: test ▸ mytestkey ``` Avalanche L1s require bootstrap validators during creation process. Avalanche CLI has enabled using local machine as a bootstrap validator on the blockchain. This means that you don't have to to set up a remote server on a cloud service (e.g. AWS / GCP) to be a validator on the blockchain. We will select `Yes` on using our local machine as a bootstrap validator. Note that since we need to sync our node with Fuji, this process will take around 3 minutes. ```bash You can use your local machine as a bootstrap validator on the blockchain This means that you don't have to to set up a remote server on a cloud service (e.g. AWS / GCP) to be a validator on the blockchain. Use the arrow keys to navigate: ↓ ↑ → ← ? Do you want to use your local machine as a bootstrap validator?: ▸ Yes No ``` Well done. You have just created your own Avalanche L1 on `Fuji`. You will be able to see information on the deployed L1 at the end of `avalanche blockchain deploy` command: ```bash +--------------------+----------------------------------------------------+ | DEPLOYMENT RESULTS | | +--------------------+----------------------------------------------------+ | Chain Name | testblockchain | +--------------------+----------------------------------------------------+ | Subnet ID | 2cNuyBhvAd4jH5bFSGndezhB66Z4UHYAsLCMGoCpvhXVhrZfgd | +--------------------+----------------------------------------------------+ | VM ID | qcvkEX1zWSz7PtGd7CKvPRBqLVTzA7qyMPvkh5NMDWkuhrcCu | +--------------------+----------------------------------------------------+ | Blockchain ID | 2U7vNdB78xTiN6QtZ9aetfKoGtQhfeEPQG6QZC8bpq8QMf4cDx | +--------------------+ + | P-Chain TXID | | +--------------------+----------------------------------------------------+ ``` To get your new Avalanche L1 information, visit the [Avalanche L1 Explorer](https://subnets-test.avax.network/). The search best works by blockchain ID, so in this example, enter `2U7vNdB78xTiN6QtZ9aetfKoGtQhfeEPQG6QZC8bpq8QMf4cDx` into the search box and you should see your shiny new blockchain information. Add a Validator[​](#add-a-validator "Direct link to heading") ------------------------------------------------------------- Before proceeding to add a validator to our Avalanche L1, we will need to have the validator's NodeID, BLS public key and proof of possession. These can be obtained by ssh into the node itself and run the `getNodeID` API specified [here](/docs/rpcs/other/info-rpc#infogetnodeid). To add a validator to an Avalanche L1, the owner of the key that acts as the controller of `ValidatorManager` contract specified in `avalanche blockchain create` command above run: ```bash avalanche blockchain addValidator testblockchain ``` Choose `Fuji`: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to deploy on: ▸ Fuji ``` You will need to specify which private key to use to pay for the transaction fees: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Which key should be used to pay for transaction fees on P-Chain?: test ▸ mytestkey ``` Now enter the **NodeID** of the new validator to be added. ```bash What is the NodeID of the validator you'd like to whitelist?: NodeID-BFa1paAAAAAAAAAAAAAAAAAAAAQGjPhUy ``` Next, enter the node's BLS public key and proof of possession. Now, enter the amount of AVAX that you would like to allocate to the new validator. The validator's balance is used to pay for continuous fee to the P-Chain. When this Balance reaches 0, the validator will be considered inactive and will no longer participate in validating the L1. 1 AVAX should last the validator about a month. ```bash What balance would you like to assign to the validator (in AVAX)?: 1 ``` Next, select a key that will receive the leftover AVAX if the validator is removed from the L1: ```bash Which stored key should be used be set as a change owner for leftover AVAX?: test ▸ mytestkey ``` Next, select a key that can remove the validator: ```bash ? Which stored key should be used be able to disable the validator using P-Chain transactions?: test ▸ mytestkey ``` By the end of the command, you would have successfully added a new validator to the Avalanche L1 on Fuji Testnet! Appendix[​](#appendix "Direct link to heading") ----------------------------------------------- ### Connect with Core[​](#connect-with-core "Direct link to heading") To connect Core (or MetaMask) with your blockchain on the new Avalanche L1 running on your local computer, you can add a new network on your Core wallet with the following values: ```bash - Network Name: testblockchain - RPC URL: [http://127.0.0.1:9650/ext/bc/2U7vNdB78xTiN6QtZ9aetfKoGtQhfeEPQG6QZC8bpq8QMf4cDx/rpc] - Chain ID: 3333 - Symbol: TST ``` # On Avalanche Mainnet (/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-mainnet) --- title: On Avalanche Mainnet description: Deploy an Avalanche L1 to Avalanche Mainnet --- Deploying an Avalanche L1 to Mainnet has many risks. Doing so safely requires a laser focus on security. This tutorial does its best to point out common pitfalls, but there may be other risks not discussed here. This tutorial is an educational resource and provides no guarantees that following it results in a secure deployment. Additionally, this tutorial takes some shortcuts that aid the understanding of the deployment process at the expense of security. The text highlights these shortcuts and they shouldn't be used for a production deployment. After managing a successful Avalanche L1 deployment on the `Fuji Testnet`, you're ready to deploy your Avalanche L1 on Mainnet. If you haven't done so, first [Deploy an Avalanche L1 on Testnet](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet). This tutorial shows how to do the following on `Mainnet`. - Deploy an Avalanche L1. - Add a node as a validator to the Avalanche L1. All IDs in this article are for illustration purposes only. They are guaranteed to be different in your own run-through of this tutorial. ## Prerequisites - An Avalanche node running and [fully bootstrapped](/docs/nodes) on `Mainnet` - [Avalanche-CLI is installed](/docs/tooling/avalanche-cli) on each validator node's box - A [Ledger](https://www.ledger.com/) device - You've [created an Avalanche L1 configuration](/docs/tooling/avalanche-cli#create-your-avalanche-l1-configuration) and fully tested a [Fuji Testnet Avalanche L1 deployment](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet) ### Setting up Your Ledger[​](#setting-up-your-ledger "Direct link to heading") In the interest of security, all Avalanche-CLI `Mainnet` operations require the use of a connected Ledger device. You must unlock your Ledger and run the Avalanche App. See [How to Use Ledger](https://support.avax.network/en/articles/6150237-how-to-use-a-ledger-nano-s-or-nano-x-with-avalanche) for help getting set up. Ledger devices support TX signing for any address inside a sequence automatically generated by the device. By default, Avalanche-CLI uses the first address of the derivation, and that address needs funds to issue the TXs to create the Avalanche L1 and add validators. To get the first `Mainnet` address of your Ledger device, first make sure it is connected, unblocked, and running the Avalanche app. Then execute the `key list` command: ```bash avalanche key list --ledger 0 --mainnet ``` ```bash +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | KIND | NAME | CHAIN | ADDRESS | BALANCE | NETWORK | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | ledger | index 0 | P-Chain (Bech32 format) | P-avax1ucykh6ls8thqpuwhg3vp8vvu6spg5e8tp8a25j | 11 | Mainnet | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ ``` The command prints the P-Chain address for `Mainnet`, `P-avax1ucykh6ls8thqpuwhg3vp8vvu6spg5e8tp8a25j`, and its balance. You can use the `key list` command to get any Ledger address in the derivation sequence by changing the index parameter from `0` to the one desired, or to a list of them (for example: `2`, or `0,4,7`). Also, you can ask for addresses on `Mainnet` with the `--mainnet` parameter, and local networks with the `--local` parameter. #### Funding the Ledger[​](#funding-the-ledger "Direct link to heading") A new Ledger device has no funds on the addresses it controls. You'll need to send funds to it by exporting them from C-Chain to the P-Chain using [Core web](https://core.app/) connected to [Core extension](https://core.app/). You can load the Ledger's C-Chain address in Core extension, or load in a different private key to [Core extension](https://core.app/), and then connect to Core web . You can move test funds from the C-Chain to the P-Chain by clicking Stake on Core web , then Cross-Chain Transfer (find more details on [this tutorial](https://support.avax.network/en/articles/8133713-core-web-how-do-i-make-cross-chain-transfers-in-core-stake)). Deploy the Avalanche L1[​](#deploy-the-avalanche-l1 "Direct link to heading") ----------------------------------------------------------------- To deploy the Avalanche L1, you will need some AVAX on the P-Chain. For our Fuji example, we used our local machine as a bootstrap validator. However, since bootstrapping a node to Mainnet will take several hours, we will use an Avalanche node set up on an AWS server that is already bootstrapped to Mainnet for this example. To check if the Avalanche node is done bootstrapping, ssh into the node and call [`info.isBootstrapped`](/docs/rpcs/other/info-rpc#infoisbootstrapped) by copying and pasting the following command: ```bash curl -X POST --data '{ "jsonrpc":"2.0", "id" :1, "method" :"info.isBootstrapped", "params": { "chain":"P" } }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info ``` If this returns `true`, it means that the chain is bootstrapped and we will proceed to deploying our L1. We will need to have the Avalanche node's NodeID, BLS public key and proof of possession. These can be obtained by ssh into the node itself and run the `getNodeID` API specified [here](/docs/rpcs/other/info-rpc#infogetnodeid) To deploy the new Avalanche L1, with your Ledger unlocked and running the Avalanche app, run: ```bash avalanche blockchain deploy testblockchain ``` This is going to start a new prompt series. ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to deploy on: Local Network Fuji ▸ Mainnet ``` This tutorial is about deploying to `Mainnet`, so navigate with the arrow keys to `Mainnet` and hit enter. The user is then asked to provide which private key to use for the deployment. Select a key to has P-Chain AVAX to pay for transaction fees. ```bash ✔ Mainnet Deploying [testblockchain] to Mainnet ``` After that, CLI shows the `Mainnet` Ledger address used to fund the deployment: ```bash Ledger address: P-avax1ucykh6ls8thqpuwhg3vp8vvu6spg5e8tp8a25j ``` Select `No` to using local machine as a bootstrap validator on the blockchain. ```bash You can use your local machine as a bootstrap validator on the blockchain This means that you don't have to to set up a remote server on a cloud service (e.g. AWS / GCP) to be a validator on the blockchain. Use the arrow keys to navigate: ↓ ↑ → ← ? Do you want to use your local machine as a bootstrap validator?: Yes ▸ No ``` Enter 1 as the number of bootstrap validators we will be setting up. ```bash ✔ No ✗ How many bootstrap validators do you want to set up?: 1 ``` Select `Yes` since we have already set up our Avalanche Node on AWS. ```bash If you have set up your own Avalanche Nodes, you can provide the Node ID and BLS Key from those nodes in the next step. Otherwise, we will generate new Node IDs and BLS Key for you. Use the arrow keys to navigate: ↓ ↑ → ← ? Have you set up your own Avalanche Nodes?: ▸ Yes No ``` Next, we will enter the node's Node-ID: ```bash Getting info for bootstrap validator 1 ✗ What is the NodeID of the node you want to add as bootstrap validator?: █ ``` And BLS public key and proof of possession: ```bash Next, we need the public key and proof of possession of the node's BLS Check https://build.avax.network/docs/rpcs/other/info-rpc#infogetnodeid for instructions on calling info.getNodeID API ✗ What is the node's BLS public key?: █ ``` Next, the CLI generates a CreateSubnet TX to create the Subnet and asks the user to sign it by using the Ledger. ```bash *** Please sign Avalanche L1 creation hash on the ledger device *** ``` This activates a `Please review` window on the Ledger. Navigate to the Ledger's `APPROVE` window by using the Ledger's right button, and then authorize the request by pressing both left and right buttons. If the Ledger doesn't have enough funds, the user may see an error message: ```bash *** Please sign Avalanche L1 creation hash on the ledger device *** Error: insufficient funds: provided UTXOs need 1000000000 more units of asset "U8iRqJoiJm8xZHAacmvYyZVwqQx6uDNtQeP3CQ6fcgQk3JqnK" ``` If successful, the CLI next asks you to sign a CreateChain Tx. Once CreateChain Tx is signed, it will then ask you to sign ConvertSubnetToL1 Tx. Well done. You have just created your own Avalanche L1 on `Mainnet`. You will be able to see information on the deployed L1 at the end of `avalanche blockchain deploy` command: ```bash +--------------------+----------------------------------------------------+ | DEPLOYMENT RESULTS | | +--------------------+----------------------------------------------------+ | Chain Name | testblockchain | +--------------------+----------------------------------------------------+ | Subnet ID | 2cNuyBhvAd4jH5bFSGndezhB66Z4UHYAsLCMGoCpvhXVhrZfgd | +--------------------+----------------------------------------------------+ | VM ID | qcvkEX1zWSz7PtGd7CKvPRBqLVTzA7qyMPvkh5NMDWkuhrcCu | +--------------------+----------------------------------------------------+ | Blockchain ID | 2U7vNdB78xTiN6QtZ9aetfKoGtQhfeEPQG6QZC8bpq8QMf4cDx | +--------------------+ + | P-Chain TXID | | +--------------------+----------------------------------------------------+ ``` To get your new Avalanche L1 information, there are two options: - Call `avalanche blockchain describe` command or - Visit the [Avalanche L1 Explorer](https://subnets-test.avax.network/). The search best works by blockchain ID, so in this example, enter `2U7vNdB78xTiN6QtZ9aetfKoGtQhfeEPQG6QZC8bpq8QMf4cDx` into the search box and you should see your shiny new blockchain information. Add a Validator[​](#add-a-validator "Direct link to heading") ------------------------------------------------------------- Before proceeding to add a validator to our Avalanche L1, we will need to have the validator's NodeID, BLS public key and proof of possession. These can be obtained by ssh into the node itself and run the `getNodeID` API specified [here](/docs/rpcs/other/info-rpc#infogetnodeid) To add a validator to an Avalanche L1, the owner of the key that acts as the controller of ValidatorManager contract specified in `avalanche blockchain create` command above run: ```bash avalanche blockchain addValidator testblockchain ``` Choose `Mainnet`: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to deploy on: ▸ Mainnet ``` The CLI will show the Ledger address that will be used to pay for add validator tx: ```bash Ledger address: P-avax1ucykh6ls8thqpuwhg3vp8vvu6spg5e8tp8a25j ``` Now enter the **NodeID** of the new validator to be added. ```bash What is the NodeID of the validator you'd like to whitelist?: NodeID-BFa1paAAAAAAAAAAAAAAAAAAAAQGjPhUy ``` Next, enter the node's BLS public key and proof of possession. Now, enter the amount of AVAX that you would like to allocate to the new validator. The validator's balance is used to pay for continuous fee to the P-Chain. When this Balance reaches 0, the validator will be considered inactive and will no longer participate in validating the L1. 1 AVAX should last the validator about a month. ```bash What balance would you like to assign to the validator (in AVAX)?: 1 ``` Sign the addValidatorTx with your Ledger: ```bash *** Please sign add validator hash on the ledger device *** ``` This activates a `Please review` window on the Ledger. Navigate to the Ledger's `APPROVE` window by using the Ledger's right button, and then authorize the request by pressing both left and right buttons. This might take a couple of seconds. After, it prints: ```bash Transaction successful, transaction ID: r3tJ4Wr2CWA8AaticmFrKdKgAs5AhW2wwWTaQHRBZKwJhsXzb ``` This means the node is now a validator on the given Avalanche L1 on `Mainnet`! Going Live[​](#going-live "Direct link to heading") --------------------------------------------------- For the safety of your validators, you should setup dedicated API nodes to process transactions, but for test purposes, you can issue transactions directly to one of your validator's RPC interface. # On Production Infra (/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-production-infra) --- title: On Production Infra description: Learn how to deploy an Avalanche L1 on production infrastructure. --- After architecting your Avalanche L1 environment on the [local machine](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-locally), proving the design and testing it out on [the Fuji Testnet](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet), eventually you will need to deploy your Avalanche L1 to production environment. Running an Avalanche L1 in production is much more involved than local and Testnet deploys, as your Avalanche L1 will have to take care of real world usage, maintaining uptime, upgrades and all of that in a potentially adversarial environment. The purpose of this document is to point out a set of general considerations and propose potential solutions to them. The architecture of the environment your particular Avalanche L1 will use will be greatly influenced by the type of load and activity your Avalanche L1 is designed to support so your solution will most likely differ from what we propose here. Still, it might be useful to follow along, to build up the intuition for the type of questions you will need to consider. Node Setup[​](#node-setup "Direct link to heading") --------------------------------------------------- Avalanche nodes are essential elements for running your Avalanche L1 in production. At a minimum, your Avalanche L1 will need validator nodes, potentially also nodes that act as RPC servers, indexers or explorers. Running a node is basically running an instance of [AvalancheGo](/docs/nodes) on a server. ### Server OS[​](#server-os "Direct link to heading") Although AvalancheGo can run on a MacOS or a Windows computer, we strongly recommend running nodes on computers running Linux as they are designed specifically for server loads and all the tools and utilities needed for administering a server are native to Linux. ### Hardware Specification[​](#hardware-specification "Direct link to heading") For running AvalancheGo as a validator on the Primary Network the recommended configuration is as follows: - CPU: Equivalent of 8 AWS vCPU - RAM: 16 GiB - Storage: 1 TiB with at least 3000 IOPS - OS: Ubuntu 20.04 - Network: Reliable IPv4 or IPv6 network connection, with an open public port That is the configuration sufficient for running a Primary Network node. Any resource requirements for your Avalanche L1 come on top of this, so you should not go below this configuration, but may need to step up the specification if you expect your Avalanche L1 to handle a significant amount of transactions. Be sure to set up monitoring of resource consumption for your nodes because resource exhaustion may cause your node to slow down or even halt, which may severely impact your Avalanche L1 negatively. ### Server Location[​](#server-location "Direct link to heading") You can run a node on a physical computer that you own and run, or on a cloud instance. Although running on your own HW may seem like a good idea, unless you have a sizeable DevOps 24/7 staff we recommend using cloud service providers as they generally provide reliable computing resources that you can count on to be properly maintained and monitored. #### Local Servers[​](#local-servers "Direct link to heading") If you plan on running nodes on your own hardware, make sure they satisfy the minimum HW specification as outlined earlier. Pay close attention to proper networking setup, making sure the p2p port (9651) is accessible and public IP properly configured on the node. Make sure the node is connected to the network physically (not over Wi-Fi), and that the router is powerful enough to handle a couple of thousands of persistent TCP connections and that network bandwidth can accommodate at least 5Mbps of steady upstream and downstream network traffic. When installing the AvalancheGo node on the machines, unless you have a dedicated DevOps staff that will take care of node setup and configuration, we recommend using the [installer script](/docs/nodes/run-a-node/using-install-script/installing-avalanche-go) to set up the nodes. It will abstract most of the setup process for you, set up the node as a system service and will enable easy node upgrades. #### Cloud Providers[​](#cloud-providers "Direct link to heading") There are a number of different cloud providers. We have documents that show how to set up a node on the most popular ones: - [Amazon Web Services](/docs/nodes/run-a-node/on-third-party-services/amazon-web-services) - [Azure](/docs/nodes/run-a-node/on-third-party-services/microsoft-azure) - [Google Cloud Platform](/docs/nodes/run-a-node/on-third-party-services/google-cloud) There is a whole range of other cloud providers that may offer lower prices or better deals for your particular needs, so it makes sense to shop around. Once you decide on a provider (or providers), if they offer instances in multiple data centers, it makes sense to spread the nodes geographically since that provides a better resilience and stability against outages. ### Number of Validators[​](#number-of-validators "Direct link to heading") Number of validators on an Avalanche L1 is a crucial decision you need to make. For stability and decentralization, you should strive to have as many validators as possible. For stability reasons our recommendation is to have **at least** 5 full validators on your Avalanche L1. If you have less than 5 validators your Avalanche L1 liveness will be at risk whenever a single validator goes offline, and if you have less than 4 even one offline node will halt your Avalanche L1. You should be aware that 5 is the minimum we recommend. But, from a decentralization standpoint having more validators is always better as it increases the stability of your Avalanche L1 and makes it more resilient to both technical failures and adversarial action. In a nutshell: run as many Avalanche L1 validators as you can. Considering that at times you will have to take nodes offline, for routine maintenance (at least for node upgrades which happen with some regularity) or unscheduled outages and failures you need to be able to routinely handle at least one node being offline without your Avalanche L1 performance degrading. ### Node Bootstrap[​](#node-bootstrap "Direct link to heading") Once you set up the server and install AvalancheGo on them, nodes will need to bootstrap (sync with the network). This is a lengthy process, as the nodes need to catch up and replay all the network activity since the genesis up to the present moment. Full bootstrap on a node can take more than a week, but there are ways to shorten that process, depending on your circumstances. #### State Sync[​](#state-sync "Direct link to heading") If the nodes you will be running as validators don't need to have the full transaction history, then you can use [state sync](/docs/nodes/chain-configs/primary-network/c-chain#state-sync-enabled). With this flag enabled, instead of replaying the whole history to get to the current state, nodes simply download only the current state from other network peers, shortening the bootstrap process from multiple days to a couple of hours. If the nodes will be used for Avalanche L1 validation exclusively, you can use the state sync without any issues. Currently, state sync is only available for the C-Chain, but since the bulk of the transactions on the platform happen there it still has a significant impact on the speed of bootstrapping. #### Database Copy[​](#database-copy "Direct link to heading") Good way to cut down on bootstrap times on multiple nodes is database copy. Database is identical across nodes, and as such can safely be copied from one node to another. Just make sure to that the node is not running during the copy process, as that can result in a corrupted database. Database copy procedure is explained in detail [here](/docs/nodes/maintain/backup-restore#database). Please make sure you don't reuse any node's NodeID by accident, especially don't restore another node's ID, see [here](/docs/nodes/maintain/backup-restore#nodeid) for details. Each node must has its own unique NodeID, otherwise, the nodes sharing the same ID will not behave correctly, which will impact your validator's uptime, thus staking rewards, and the stability of your Avalanche L1. Avalanche L1 Deploy[​](#avalanche-l1-deploy "Direct link to heading") --------------------------------------------------------- Once you have the nodes set up you are ready to deploy the actual Avalanche L1. Right now, the recommended tool to do that is [Avalanche-CLI](https://github.com/ava-labs/avalanche-cli). Instructions for deployment by Avalanche-CLI can be found [here](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-mainnet). ### Ledger Hardware Wallet[​](#ledger-hw-wallet "Direct link to heading") When creating the Avalanche L1, you will be required to have a private key that will control the administrative functions of the Avalanche L1 (adding validators, managing the configuration). Needless to say, whoever has this private key has complete control over the Avalanche L1 and the way it runs. Therefore, protecting that key is of the utmost operational importance. Which is why we strongly recommend using a hardware wallet such as a [Ledger HW Wallet](https://www.ledger.com/) to store and access that private key. General instruction on how to use a Ledger device with Avalanche can be found [here](https://support.avax.network/en/articles/6150237-how-to-use-a-ledger-nano-s-or-nano-x-with-avalanche). ### Genesis File[​](#genesis-file "Direct link to heading") The structure that defines the most important parameters in an Avalanche L1 is found in the genesis file, which is a `json` formatted, human-readable file. Describing the contents and the options available in the genesis file is beyond the scope of this document, and if you're ready to deploy your Avalanche L1 to production you probably have it mapped out already. If you want to review, we have a description of the genesis file in our document on [customizing EVM Avalanche L1s](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1). Validator Configuration[​](#validator-configuration "Direct link to heading") ----------------------------------------------------------------------------- Running nodes as Avalanche L1 validators warrants some additional considerations, above those when running a regular node or a Primary Network-only validator. ### Joining an Avalanche L1[​](#joining-a-avalanche-l1 "Direct link to heading") For a node to join an Avalanche L1, there are two prerequisites: - Primary Network validation - Avalanche L1 tracking Primary Network validation means that a node cannot join an Avalanche L1 as a validator before becoming a validator on the Primary Network itself. So, after you add the node to the validator set on the Primary Network, node can join an Avalanche L1. Of course, this is valid only for Avalanche L1 validators, if you need a non-validating Avalanche L1 node, then the node doesn't need to be a validator at all. To have a node start syncing the Avalanche L1, you need to add the `--track-subnets` command line option, or `track-subnets` key to the node config file (found at `.avalanchego/configs/node.json` for installer-script created nodes). A single node can sync multiple Layer 1s, so you can add them as a comma-separated list of Avalanche L1 IDs (SubnetID). An example of a node config syncing two Avalanche L1s: ```json { "public-ip-resolution-service": "opendns", "http-host": "", "track-subnets": "28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY,Ai42MkKqk8yjXFCpoHXw7rdTWSHiKEMqh5h8gbxwjgkCUfkrk" } ``` But that is not all. Besides tracking the SubnetID, the node also needs to have the plugin that contains the VM instance the blockchain in the Avalanche L1 will run. You should have already been through that on Testnet and Fuji, but for a refresher, you can refer to [this tutorial](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet). So, name the VM plugin binary as the `VMID` of the Avalanche L1 chain and place it in the `plugins` directory where the node binary is (for installer-script created nodes that would be `~/avalanche-node/plugins/`). ### Avalanche L1 Bootstrapping[​](#avalanche-l1-bootstrapping "Direct link to heading") After you have tracked the Avalanche L1 and placed the VM binary in the correct directory, your node is ready to start syncing with the Avalanche L1. Restart the node and monitor the log output. You should notice something similar to: ```bash Jul 30 18:26:31 node-fuji avalanchego[1728308]: [07-30|18:26:31.422] INFO chains/manager.go:262 creating chain: Jul 30 18:26:31 node-fuji avalanchego[1728308]: ID: 2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt Jul 30 18:26:31 node-fuji avalanchego[1728308]: VMID:srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy ``` That means the node has detected the Avalanche L1, and is attempting to initialize it and start bootstrapping the Avalanche L1. It might take some time (if there are already transactions on the Avalanche L1), and eventually it will finish the bootstrap with a message like: ```bash Jul 30 18:27:21 node-fuji avalanchego[1728308]: [07-30|18:27:21.055] INFO <2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt Chain> snowman/transitive.go:333 consensus starting with J5wjmotMCrM2DKxeBTBPfwgCPpvsjtuqWNozLog2TomTjSuGK as the last accepted block ``` That means the node has successfully bootstrapped the Avalanche L1 and is now in sync. If the node is one of the validators, it will start validating any transactions that get posted to the Avalanche L1. ### Monitoring[​](#monitoring "Direct link to heading") If you want to inspect the process of Avalanche L1 syncing, you can use the RPC call to check for the [blockchain status](/docs/rpcs/p-chain#platformgetblockchainstatus). For a more in-depth look into Avalanche L1 operation, check out the blockchain log. By default, the log can be found in `~/.avalanchego/logs/ChainID.log` where you replace the `ChainID` with the actual ID of the blockchain in your Avalanche L1. For an even more thorough (and pretty!) insight into how the node and the Avalanche L1 is behaving, you can install the Prometheus+Grafana monitoring system with the custom dashboards for the regular node operation, as well as a dedicated dashboard for Avalanche L1 data. Check out the [tutorial](/docs/nodes/maintain/monitoring) for information on how to set it up. ### Managing Validation[​](#managing-validation "Direct link to heading") On Avalanche all validations are limited in time and can range from two weeks up to one year. Furthermore, Avalanche L1 validations are always a subset of the Primary Network validation period (must be shorter or the same). That means that periodically your validators will expire and you will need to submit a new validation transaction for both the Primary Network and your Avalanche L1. Unless managed properly and in a timely manner, that can be disruptive for your Avalanche L1 (if all validators expire at the same time your Avalanche L1 will halt). To avoid that, keep notes on when a particular validation is set to expire and be ready to renew it as soon as possible. Also, when initially setting up the nodes, make sure to stagger the validator expiry so they don't all expire on the same date. Setting end dates at least a day apart is a good practice, as well as setting reminders for each expiry. Conclusion[​](#conclusion "Direct link to heading") --------------------------------------------------- Hopefully, by reading this document you have a better picture of the requirements and considerations you need to make when deploying your Avalanche L1 to production and you are now better prepared to launch your Avalanche L1 successfully. Keep in mind, running an Avalanche L1 in production is not a one-and-done kind of situation, it is in fact running a fleet of servers 24/7. And as with any real time service, you should have a robust logging, monitoring and alerting systems to constantly check the nodes and Avalanche L1 health and alert you if anything out of the ordinary happens. If you have any questions, doubts or would like to chat, please check out our [Discord server](https://discord.gg/avax/), where we host a dedicated `#subnet-chat` channel dedicated to talking about all things Avalanche L1. # With Custom VM (/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-with-custom-vm) --- title: With Custom VM description: Learn how to create an Avalanche L1 with a custom virtual machine and deploy it locally. --- This tutorial walks through the process of creating an Avalanche L1 with a custom virtual machine and deploying it locally. Although the tutorial uses a fork of Subnet-EVM as an example, you can extend its lessons to support any custom VM binary. Fork Subnet-EVM[​](#fork-subnet-evm "Direct link to heading") ------------------------------------------------------------- Instead of building a custom VM from scratch, this tutorial starts with forking Subnet-EVM. ### Clone Subnet-EVM[​](#clone-subnet-evm "Direct link to heading") First off, clone the Subnet-EVM repository into a directory of your choosing. ```bash git clone https://github.com/ava-labs/subnet-evm.git ``` The repository cloning method used is HTTPS, but SSH can be used too: `git clone git@github.com:ava-labs/subnet-evm.git` You can find more about SSH and how to use it [here](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh). ### Modify and Build Subnet-EVM[​](#modify-and-build-subnet-evm "Direct link to heading") To prove you're running your custom binary and not the stock Subnet-EVM included with Avalanche-CLI, you need to modify the Subnet-EVM binary by making a minor change. Navigate to the directory you cloned Subnet-EVM into and generate a new commit: ```bash git commit -a --allow-empty -m "custom vm commit" ``` Take note of the new commit hash: ```bash git rev-parse HEAD c0fe6506a40da466285f37dd0d3c044f494cce32 ``` In this case, `c0fe6506a40da466285f37dd0d3c044f494cce32`. Now build your custom binary by running: ```bash ./scripts/build.sh custom_vm.bin ``` This command builds the binary and saves it at `./custom_vm.bin`. ### Create a Custom Genesis[​](#create-a-custom-genesis "Direct link to heading") To start a VM, you need to provide a genesis file. Here is a basic Subnet-EVM genesis that's compatible with your custom VM. This genesis includes the PoA Validator Manager, a Transparent Proxy and Proxy Admin as predeployed contracts. `0c0deba5e0000000000000000000000000000000` is the ValidatorManager address. `0feedc0de0000000000000000000000000000000` is the Transparent Proxy address. `c0ffee1234567890abcdef1234567890abcdef34` is the Proxy Admin contract. These proxy contracts are from [OpenZeppelin v4.9](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/release-v4.9/contracts/proxy/transparent) You can add your own predeployed contracts by running `forge build` and collecting the `deployedBytecode` output from `out/MyContract.sol` ```json { "config": { "berlinBlock": 0, "byzantiumBlock": 0, "chainId": 1, "constantinopleBlock": 0, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0, "feeConfig": { "gasLimit": 12000000, "targetBlockRate": 2, "minBaseFee": 25000000000, "targetGas": 60000000, "baseFeeChangeDenominator": 36, "minBlockGasCost": 0, "maxBlockGasCost": 1000000, "blockGasCostStep": 200000 }, "homesteadBlock": 0, "istanbulBlock": 0, "londonBlock": 0, "muirGlacierBlock": 0, "petersburgBlock": 0, "warpConfig": { "blockTimestamp": 1736356569, "quorumNumerator": 67, "requirePrimaryNetworkSigners": true } }, "nonce": "0x0", "timestamp": "0x677eb2d9", "extraData": "0x", "gasLimit": "0xb71b00", "difficulty": "0x0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "alloc": { "0c0deba5e0000000000000000000000000000000": { "code": "0x608060405234801561000f575f80fd5b5060043610610132575f3560e01c80639ba96b86116100b4578063c974d1b611610079578063c974d1b6146102a7578063d588c18f146102af578063d5f20ff6146102c2578063df93d8de146102e2578063f2fde38b146102ec578063fd7ac5e7146102ff575f80fd5b80639ba96b861461024c578063a3a65e481461025f578063b771b3bc14610272578063bc5fbfec14610280578063bee0a03f14610294575f80fd5b8063715018a6116100fa578063715018a6146101be578063732214f8146101c65780638280a25a146101db5780638da5cb5b146101f557806397fb70d414610239575f80fd5b80630322ed981461013657806320d91b7a1461014b578063467ef06f1461015e57806360305d621461017157806366435abf14610193575b5f80fd5b610149610144366004612b01565b610312565b005b610149610159366004612b30565b610529565b61014961016c366004612b7e565b610a15565b610179601481565b60405163ffffffff90911681526020015b60405180910390f35b6101a66101a1366004612b01565b610a23565b6040516001600160401b03909116815260200161018a565b610149610a37565b6101cd5f81565b60405190815260200161018a565b6101e3603081565b60405160ff909116815260200161018a565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03165b6040516001600160a01b03909116815260200161018a565b610149610247366004612b01565b610a4a565b6101cd61025a366004612bad565b610a5f565b61014961026d366004612b7e565b610a7b565b6102216005600160991b0181565b6101cd5f8051602061370d83398151915281565b6101496102a2366004612b01565b610c04565b6101e3601481565b6101496102bd366004612c06565b610d41565b6102d56102d0366004612b01565b610e4f565b60405161018a9190612cc3565b6101a66202a30081565b6101496102fa366004612d43565b610f9e565b6101cd61030d366004612d65565b610fdb565b5f8181525f8051602061372d8339815191526020526040808220815160e0810190925280545f8051602061370d83398151915293929190829060ff16600581111561035f5761035f612c42565b600581111561037057610370612c42565b815260200160018201805461038490612dd0565b80601f01602080910402602001604051908101604052809291908181526020018280546103b090612dd0565b80156103fb5780601f106103d2576101008083540402835291602001916103fb565b820191905f5260205f20905b8154815290600101906020018083116103de57829003601f168201915b505050918352505060028201546001600160401b038082166020840152600160401b820481166040840152600160801b820481166060840152600160c01b909104811660808301526003928301541660a0909101529091508151600581111561046657610466612c42565b146104a2575f8381526007830160205260409081902054905163170cc93360e21b81526104999160ff1690600401612e08565b60405180910390fd5b6005600160991b016001600160a01b031663ee5b48eb6104c78584606001515f611036565b6040518263ffffffff1660e01b81526004016104e39190612e16565b6020604051808303815f875af11580156104ff573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105239190612e28565b50505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb09545f8051602061370d8339815191529060ff161561057b57604051637fab81e560e01b815260040160405180910390fd5b6005600160991b016001600160a01b0316634213cf786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105e29190612e28565b83602001351461060b576040516372b0a7e760e11b815260208401356004820152602401610499565b3061061c6060850160408601612d43565b6001600160a01b03161461065f5761063a6060840160408501612d43565b604051632f88120d60e21b81526001600160a01b039091166004820152602401610499565b5f61066d6060850185612e3f565b905090505f805b828163ffffffff161015610955575f6106906060880188612e3f565b8363ffffffff168181106106a6576106a6612e84565b90506020028101906106b89190612e98565b6106c190612fbc565b80516040519192505f9160088801916106d991613035565b9081526020016040518091039020541461070957805160405163a41f772f60e01b81526104999190600401612e16565b5f6002885f01358460405160200161073892919091825260e01b6001600160e01b031916602082015260240190565b60408051601f198184030181529082905261075291613035565b602060405180830381855afa15801561076d573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906107909190612e28565b90508086600801835f01516040516107a89190613035565b90815260408051602092819003830181209390935560e0830181526002835284518284015284810180516001600160401b03908116858401525f60608601819052915181166080860152421660a085015260c0840181905284815260078a01909252902081518154829060ff1916600183600581111561082a5761082a612c42565b0217905550602082015160018201906108439082613091565b506040828101516002830180546060860151608087015160a08801516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909301516003909201805467ffffffffffffffff1916928416929092179091558301516108e8911685613164565b82516040519195506108f991613035565b60408051918290038220908401516001600160401b031682529082907f9d47fef9da077661546e646d61830bfcbda90506c2e5eed38195e82c4eb1cbdf9060200160405180910390a350508061094e90613177565b9050610674565b50600483018190555f61097361096a86611085565b6040015161119b565b90505f61097f87611328565b90505f6002826040516109929190613035565b602060405180830381855afa1580156109ad573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906109d09190612e28565b90508281146109fc57604051631872fc8d60e01b81526004810182905260248101849052604401610499565b5050506009909201805460ff1916600117905550505050565b610a1e81611561565b505050565b5f610a2d82610e4f565b6080015192915050565b610a3f61189f565b610a485f6118fa565b565b610a5261189f565b610a5b8161196a565b5050565b5f610a6861189f565b610a728383611c4e565b90505b92915050565b5f8051602061370d8339815191525f80610aa0610a9785611085565b604001516121a1565b9150915080610ac657604051632d07135360e01b81528115156004820152602401610499565b5f82815260068401602052604090208054610ae090612dd0565b90505f03610b045760405163089938b360e11b815260048101839052602401610499565b60015f83815260078501602052604090205460ff166005811115610b2a57610b2a612c42565b14610b5d575f8281526007840160205260409081902054905163170cc93360e21b81526104999160ff1690600401612e08565b5f8281526006840160205260408120610b7591612a75565b5f828152600784016020908152604091829020805460ff1916600290811782550180546001600160401b0342818116600160c01b026001600160c01b0390931692909217928390558451600160801b9093041682529181019190915283917ff8fd1c90fb9cfa2ca2358fdf5806b086ad43315d92b221c929efc7f105ce7568910160405180910390a250505050565b5f8181527fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb066020526040902080545f8051602061370d8339815191529190610c4b90612dd0565b90505f03610c6f5760405163089938b360e11b815260048101839052602401610499565b60015f83815260078301602052604090205460ff166005811115610c9557610c95612c42565b14610cc8575f8281526007820160205260409081902054905163170cc93360e21b81526104999160ff1690600401612e08565b5f82815260068201602052604090819020905163ee5b48eb60e01b81526005600160991b019163ee5b48eb91610d019190600401613199565b6020604051808303815f875af1158015610d1d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a1e9190612e28565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f81158015610d855750825b90505f826001600160401b03166001148015610da05750303b155b905081158015610dae575080155b15610dcc5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610df657845460ff60401b1916600160401b1785555b610e00878761235d565b8315610e4657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b610e57612aac565b5f8281525f8051602061372d833981519152602052604090819020815160e0810190925280545f8051602061370d833981519152929190829060ff166005811115610ea457610ea4612c42565b6005811115610eb557610eb5612c42565b8152602001600182018054610ec990612dd0565b80601f0160208091040260200160405190810160405280929190818152602001828054610ef590612dd0565b8015610f405780601f10610f1757610100808354040283529160200191610f40565b820191905f5260205f20905b815481529060010190602001808311610f2357829003601f168201915b505050918352505060028201546001600160401b038082166020840152600160401b820481166040840152600160801b820481166060840152600160c01b9091048116608083015260039092015490911660a0909101529392505050565b610fa661189f565b6001600160a01b038116610fcf57604051631e4fbdf760e01b81525f6004820152602401610499565b610fd8816118fa565b50565b6040515f905f8051602061370d833981519152907fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb089061101e9086908690613223565b90815260200160405180910390205491505092915050565b604080515f6020820152600360e01b602282015260268101949094526001600160c01b031960c093841b811660468601529190921b16604e830152805180830360360181526056909201905290565b60408051606080820183525f8083526020830152918101919091526040516306f8253560e41b815263ffffffff831660048201525f9081906005600160991b0190636f825350906024015f60405180830381865afa1580156110e9573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526111109190810190613241565b915091508061113257604051636b2f19e960e01b815260040160405180910390fd5b815115611158578151604051636ba589a560e01b81526004810191909152602401610499565b60208201516001600160a01b031615611194576020820151604051624de75d60e31b81526001600160a01b039091166004820152602401610499565b5092915050565b5f81516026146111d057815160405163cc92daa160e01b815263ffffffff909116600482015260266024820152604401610499565b5f805b600281101561121f576111e7816001613313565b6111f2906008613326565b61ffff1684828151811061120857611208612e84565b016020015160f81c901b91909117906001016111d3565b5061ffff8116156112495760405163407b587360e01b815261ffff82166004820152602401610499565b5f805b60048110156112a457611260816003613313565b61126b906008613326565b63ffffffff168561127d836002613164565b8151811061128d5761128d612e84565b016020015160f81c901b919091179060010161124c565b5063ffffffff8116156112ca57604051635b60892f60e01b815260040160405180910390fd5b5f805b602081101561131f576112e181601f613313565b6112ec906008613326565b866112f8836006613164565b8151811061130857611308612e84565b016020015160f81c901b91909117906001016112cd565b50949350505050565b60605f8083356020850135601461134487870160408901612d43565b6113516060890189612e3f565b60405160f09790971b6001600160f01b0319166020880152602287019590955250604285019290925260e090811b6001600160e01b0319908116606286015260609290921b6bffffffffffffffffffffffff191660668501529190911b16607a820152607e0160405160208183030381529060405290505f5b6113d76060850185612e3f565b9050811015611194576113ed6060850185612e3f565b828181106113fd576113fd612e84565b905060200281019061140f9190612e98565b61141d90602081019061333d565b905060301461143f5760405163180ffa0d60e01b815260040160405180910390fd5b8161144d6060860186612e3f565b8381811061145d5761145d612e84565b905060200281019061146f9190612e98565b611479908061333d565b90506114886060870187612e3f565b8481811061149857611498612e84565b90506020028101906114aa9190612e98565b6114b4908061333d565b6114c16060890189612e3f565b868181106114d1576114d1612e84565b90506020028101906114e39190612e98565b6114f190602081019061333d565b6114fe60608b018b612e3f565b8881811061150e5761150e612e84565b90506020028101906115209190612e98565b61153190606081019060400161337f565b6040516020016115479796959493929190613398565b60408051601f1981840301815291905291506001016113ca565b5f61156a612aac565b5f8051602061370d8339815191525f80611586610a9787611085565b9150915080156115ad57604051632d07135360e01b81528115156004820152602401610499565b5f828152600784016020526040808220815160e081019092528054829060ff1660058111156115de576115de612c42565b60058111156115ef576115ef612c42565b815260200160018201805461160390612dd0565b80601f016020809104026020016040519081016040528092919081815260200182805461162f90612dd0565b801561167a5780601f106116515761010080835404028352916020019161167a565b820191905f5260205f20905b81548152906001019060200180831161165d57829003601f168201915b505050918352505060028201546001600160401b038082166020840152600160401b820481166040840152600160801b820481166060840152600160c01b909104811660808301526003928301541660a090910152909150815160058111156116e5576116e5612c42565b14158015611706575060018151600581111561170357611703612c42565b14155b1561172757805160405163170cc93360e21b81526104999190600401612e08565b60038151600581111561173c5761173c612c42565b0361174a576004815261174f565b600581525b8360080181602001516040516117659190613035565b90815260408051602092819003830190205f908190558581526007870190925290208151815483929190829060ff191660018360058111156117a9576117a9612c42565b0217905550602082015160018201906117c29082613091565b5060408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff1916919092161790558051600581111561186857611868612c42565b60405184907f1c08e59656f1a18dc2da76826cdc52805c43e897a17c50faefb8ab3c1526cc16905f90a39196919550909350505050565b336118d17f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610a485760405163118cdaa760e01b8152336004820152602401610499565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b611972612aac565b5f8281525f8051602061372d8339815191526020526040808220815160e0810190925280545f8051602061370d83398151915293929190829060ff1660058111156119bf576119bf612c42565b60058111156119d0576119d0612c42565b81526020016001820180546119e490612dd0565b80601f0160208091040260200160405190810160405280929190818152602001828054611a1090612dd0565b8015611a5b5780601f10611a3257610100808354040283529160200191611a5b565b820191905f5260205f20905b815481529060010190602001808311611a3e57829003601f168201915b50505091835250506002828101546001600160401b038082166020850152600160401b820481166040850152600160801b820481166060850152600160c01b9091048116608084015260039093015490921660a09091015290915081516005811115611ac957611ac9612c42565b14611afc575f8481526007830160205260409081902054905163170cc93360e21b81526104999160ff1690600401612e08565b60038152426001600160401b031660c08201525f84815260078301602052604090208151815483929190829060ff19166001836005811115611b4057611b40612c42565b021790555060208201516001820190611b599082613091565b5060408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff1916919092161790555f611bf78582612377565b6080840151604080516001600160401b03909216825242602083015291935083925087917f13d58394cf269d48bcf927959a29a5ffee7c9924dafff8927ecdf3c48ffa7c67910160405180910390a3509392505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb09545f9060ff16611c9257604051637fab81e560e01b815260040160405180910390fd5b5f8051602061370d83398151915242611cb1606086016040870161337f565b6001600160401b0316111580611ceb5750611ccf6202a30042613164565b611cdf606086016040870161337f565b6001600160401b031610155b15611d2557611d00606085016040860161337f565b604051635879da1360e11b81526001600160401b039091166004820152602401610499565b6030611d34602086018661333d565b905014611d6657611d48602085018561333d565b6040516326475b2f60e11b8152610499925060040190815260200190565b611d70848061333d565b90505f03611d9d57611d82848061333d565b604051633e08a12560e11b8152600401610499929190613401565b5f60088201611dac868061333d565b604051611dba929190613223565b90815260200160405180910390205414611df357611dd8848061333d565b60405163a41f772f60e01b8152600401610499929190613401565b611dfd835f6124ce565b6040805160e08101909152815481525f908190611f099060208101611e22898061333d565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250505090825250602090810190611e6a908a018a61333d565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250505090825250602001611eb360608a0160408b0161337f565b6001600160401b03168152602001611ece60608a018a61342f565b611ed790613443565b8152602001611ee960808a018a61342f565b611ef290613443565b8152602001876001600160401b03168152506126a8565b5f82815260068601602052604090209193509150611f278282613091565b508160088401611f37888061333d565b604051611f45929190613223565b9081526040519081900360200181209190915563ee5b48eb60e01b81525f906005600160991b019063ee5b48eb90611f81908590600401612e16565b6020604051808303815f875af1158015611f9d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fc19190612e28565b6040805160e081019091529091508060018152602001611fe1898061333d565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201829052509385525050506001600160401b0389166020808401829052604080850184905260608501929092526080840183905260a0909301829052868252600788019092522081518154829060ff1916600183600581111561207057612070612c42565b0217905550602082015160018201906120899082613091565b5060408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff19169190921617905580612127888061333d565b604051612135929190613223565b6040518091039020847fb77297e3befc691bfc864a81e241f83e2ef722b6e7becaa2ecec250c6d52b430898b6040016020810190612173919061337f565b604080516001600160401b0393841681529290911660208301520160405180910390a4509095945050505050565b5f8082516027146121d757825160405163cc92daa160e01b815263ffffffff909116600482015260276024820152604401610499565b5f805b6002811015612226576121ee816001613313565b6121f9906008613326565b61ffff1685828151811061220f5761220f612e84565b016020015160f81c901b91909117906001016121da565b5061ffff8116156122505760405163407b587360e01b815261ffff82166004820152602401610499565b5f805b60048110156122ab57612267816003613313565b612272906008613326565b63ffffffff1686612284836002613164565b8151811061229457612294612e84565b016020015160f81c901b9190911790600101612253565b5063ffffffff81166002146122d357604051635b60892f60e01b815260040160405180910390fd5b5f805b6020811015612328576122ea81601f613313565b6122f5906008613326565b87612301836006613164565b8151811061231157612311612e84565b016020015160f81c901b91909117906001016122d6565b505f8660268151811061233d5761233d612e84565b016020015191976001600160f81b03199092161515965090945050505050565b612365612895565b61236e826128de565b610a5b816128f7565b5f8281525f8051602061372d833981519152602052604081206002015481905f8051602061370d83398151915290600160801b90046001600160401b03166123bf85826124ce565b5f6123c987612908565b5f8881526007850160205260408120600201805467ffffffffffffffff60801b1916600160801b6001600160401b038b16021790559091506005600160991b0163ee5b48eb6124198a858b611036565b6040518263ffffffff1660e01b81526004016124359190612e16565b6020604051808303815f875af1158015612451573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124759190612e28565b604080516001600160401b038a811682526020820184905282519394508516928b927f07de5ff35a674a8005e661f3333c907ca6333462808762d19dc7b3abb1a8c1df928290030190a3909450925050505b9250929050565b5f8051602061370d8339815191525f6001600160401b038084169085161115612502576124fb838561350a565b905061250f565b61250c848461350a565b90505b6040805160808101825260028401548082526003850154602083015260048501549282019290925260058401546001600160401b031660608201524291158061257157506001840154815161256d916001600160401b031690613164565b8210155b15612597576001600160401b0383166060820152818152604081015160208201526125b6565b82816060018181516125a9919061352a565b6001600160401b03169052505b60608101516125c690606461354a565b602082015160018601546001600160401b0392909216916125f19190600160401b900460ff16613326565b101561262157606081015160405163dfae880160e01b81526001600160401b039091166004820152602401610499565b856001600160401b03168160400181815161263c9190613164565b9052506040810180516001600160401b038716919061265c908390613313565b905250805160028501556020810151600385015560408101516004850155606001516005909301805467ffffffffffffffff19166001600160401b039094169390931790925550505050565b5f60608260400151516030146126d15760405163180ffa0d60e01b815260040160405180910390fd5b82516020808501518051604080880151606089015160808a01518051908701515193515f98612712988a986001989297929690959094909390929101613575565b60405160208183030381529060405290505f5b846080015160200151518110156127845781856080015160200151828151811061275157612751612e84565b602002602001015160405160200161276a92919061362f565b60408051601f198184030181529190529150600101612725565b5060a08401518051602091820151516040516127a4938593929101613665565b60405160208183030381529060405290505f5b8460a00151602001515181101561281657818560a001516020015182815181106127e3576127e3612e84565b60200260200101516040516020016127fc92919061362f565b60408051601f1981840301815291905291506001016127b7565b5060c084015160405161282d9183916020016136a0565b604051602081830303815290604052905060028160405161284e9190613035565b602060405180830381855afa158015612869573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061288c9190612e28565b94909350915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610a4857604051631afcd79f60e31b815260040160405180910390fd5b6128e6612895565b6128ee61297d565b610fd881612985565b6128ff612895565b610fd881612a6d565b5f8181525f8051602061372d8339815191526020526040812060020180545f8051602061370d833981519152919060089061295290600160401b90046001600160401b03166136d1565b91906101000a8154816001600160401b0302191690836001600160401b031602179055915050919050565b610a48612895565b61298d612895565b80355f8051602061370d83398151915290815560146129b260608401604085016136ec565b60ff1611806129d157506129cc60608301604084016136ec565b60ff16155b15612a05576129e660608301604084016136ec565b604051634a59bbff60e11b815260ff9091166004820152602401610499565b612a1560608301604084016136ec565b60018201805460ff92909216600160401b0260ff60401b19909216919091179055612a46604083016020840161337f565b600191909101805467ffffffffffffffff19166001600160401b0390921691909117905550565b610fa6612895565b508054612a8190612dd0565b5f825580601f10612a90575050565b601f0160209004905f5260205f2090810190610fd89190612ae9565b6040805160e08101909152805f81526060602082018190525f604083018190529082018190526080820181905260a0820181905260c09091015290565b5b80821115612afd575f8155600101612aea565b5090565b5f60208284031215612b11575f80fd5b5035919050565b803563ffffffff81168114612b2b575f80fd5b919050565b5f8060408385031215612b41575f80fd5b82356001600160401b03811115612b56575f80fd5b830160808186031215612b67575f80fd5b9150612b7560208401612b18565b90509250929050565b5f60208284031215612b8e575f80fd5b610a7282612b18565b80356001600160401b0381168114612b2b575f80fd5b5f8060408385031215612bbe575f80fd5b82356001600160401b03811115612bd3575f80fd5b830160a08186031215612be4575f80fd5b9150612b7560208401612b97565b6001600160a01b0381168114610fd8575f80fd5b5f808284036080811215612c18575f80fd5b6060811215612c25575f80fd5b508291506060830135612c3781612bf2565b809150509250929050565b634e487b7160e01b5f52602160045260245ffd5b60068110612c7257634e487b7160e01b5f52602160045260245ffd5b9052565b5f5b83811015612c90578181015183820152602001612c78565b50505f910152565b5f8151808452612caf816020860160208601612c76565b601f01601f19169290920160200192915050565b60208152612cd5602082018351612c56565b5f602083015160e06040840152612cf0610100840182612c98565b905060408401516001600160401b0380821660608601528060608701511660808601528060808701511660a08601528060a08701511660c08601528060c08701511660e086015250508091505092915050565b5f60208284031215612d53575f80fd5b8135612d5e81612bf2565b9392505050565b5f8060208385031215612d76575f80fd5b82356001600160401b0380821115612d8c575f80fd5b818501915085601f830112612d9f575f80fd5b813581811115612dad575f80fd5b866020828501011115612dbe575f80fd5b60209290920196919550909350505050565b600181811c90821680612de457607f821691505b602082108103612e0257634e487b7160e01b5f52602260045260245ffd5b50919050565b60208101610a758284612c56565b602081525f610a726020830184612c98565b5f60208284031215612e38575f80fd5b5051919050565b5f808335601e19843603018112612e54575f80fd5b8301803591506001600160401b03821115612e6d575f80fd5b6020019150600581901b36038213156124c7575f80fd5b634e487b7160e01b5f52603260045260245ffd5b5f8235605e19833603018112612eac575f80fd5b9190910192915050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b0381118282101715612eec57612eec612eb6565b60405290565b604080519081016001600160401b0381118282101715612eec57612eec612eb6565b604051601f8201601f191681016001600160401b0381118282101715612f3c57612f3c612eb6565b604052919050565b5f6001600160401b03821115612f5c57612f5c612eb6565b50601f01601f191660200190565b5f82601f830112612f79575f80fd5b8135612f8c612f8782612f44565b612f14565b818152846020838601011115612fa0575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60608236031215612fcc575f80fd5b612fd4612eca565b82356001600160401b0380821115612fea575f80fd5b612ff636838701612f6a565b8352602085013591508082111561300b575f80fd5b5061301836828601612f6a565b60208301525061302a60408401612b97565b604082015292915050565b5f8251612eac818460208701612c76565b601f821115610a1e57805f5260205f20601f840160051c8101602085101561306b5750805b601f840160051c820191505b8181101561308a575f8155600101613077565b5050505050565b81516001600160401b038111156130aa576130aa612eb6565b6130be816130b88454612dd0565b84613046565b602080601f8311600181146130f1575f84156130da5750858301515b5f19600386901b1c1916600185901b178555613148565b5f85815260208120601f198616915b8281101561311f57888601518255948401946001909101908401613100565b508582101561313c57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610a7557610a75613150565b5f63ffffffff80831681810361318f5761318f613150565b6001019392505050565b5f60208083525f84546131ab81612dd0565b806020870152604060018084165f81146131cc57600181146131e857613215565b60ff19851660408a0152604084151560051b8a01019550613215565b895f5260205f205f5b8581101561320c5781548b82018601529083019088016131f1565b8a016040019650505b509398975050505050505050565b818382375f9101908152919050565b80518015158114612b2b575f80fd5b5f8060408385031215613252575f80fd5b82516001600160401b0380821115613268575f80fd5b908401906060828703121561327b575f80fd5b613283612eca565b8251815260208084015161329681612bf2565b828201526040840151838111156132ab575f80fd5b80850194505087601f8501126132bf575f80fd5b835192506132cf612f8784612f44565b83815288828587010111156132e2575f80fd5b6132f184838301848801612c76565b80604084015250819550613306818801613232565b9450505050509250929050565b81810381811115610a7557610a75613150565b8082028115828204841417610a7557610a75613150565b5f808335601e19843603018112613352575f80fd5b8301803591506001600160401b0382111561336b575f80fd5b6020019150368190038213156124c7575f80fd5b5f6020828403121561338f575f80fd5b610a7282612b97565b5f88516133a9818460208d01612c76565b60e089901b6001600160e01b031916908301908152868860048301378681019050600481015f8152858782375060c09390931b6001600160c01b0319166004939094019283019390935250600c019695505050505050565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b5f8235603e19833603018112612eac575f80fd5b5f60408236031215613453575f80fd5b61345b612ef2565b61346483612b18565b81526020808401356001600160401b0380821115613480575f80fd5b9085019036601f830112613492575f80fd5b8135818111156134a4576134a4612eb6565b8060051b91506134b5848301612f14565b81815291830184019184810190368411156134ce575f80fd5b938501935b838510156134f857843592506134e883612bf2565b82825293850193908501906134d3565b94860194909452509295945050505050565b6001600160401b0382811682821603908082111561119457611194613150565b6001600160401b0381811683821601908082111561119457611194613150565b6001600160401b0381811683821602808216919082811461356d5761356d613150565b505092915050565b61ffff60f01b8a60f01b1681525f63ffffffff60e01b808b60e01b166002840152896006840152808960e01b1660268401525086516135bb81602a850160208b01612c76565b8651908301906135d281602a840160208b01612c76565b60c087901b6001600160c01b031916602a9290910191820152613604603282018660e01b6001600160e01b0319169052565b61361d603682018560e01b6001600160e01b0319169052565b603a019b9a5050505050505050505050565b5f8351613640818460208801612c76565b60609390931b6bffffffffffffffffffffffff19169190920190815260140192915050565b5f8451613676818460208901612c76565b6001600160e01b031960e095861b8116919093019081529290931b16600482015260080192915050565b5f83516136b1818460208801612c76565b60c09390931b6001600160c01b0319169190920190815260080192915050565b5f6001600160401b0380831681810361318f5761318f613150565b5f602082840312156136fc575f80fd5b813560ff81168114612d5e575f80fdfee92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb00e92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb07a164736f6c6343000819000a", "balance": "0x0", "nonce": "0x1" }, "0feedc0de0000000000000000000000000000000": { "code": "0x60806040523661001357610011610017565b005b6100115b61001f610169565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a5761005361019c565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a576100536101f3565b63070d7c6960e41b6001600160e01b031982160161009a57610053610239565b621eb96f60e61b6001600160e01b03198216016100b95761005361026a565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102aa565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102be565b565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101a66102ce565b60006101b53660048184610683565b8101906101c291906106c9565b90506101df816040518060200160405280600081525060006102d9565b505060408051602081019091526000815290565b60606000806102053660048184610683565b81019061021291906106fa565b91509150610222828260016102d9565b604051806020016040528060008152509250505090565b60606102436102ce565b60006102523660048184610683565b81019061025f91906106c9565b90506101df81610305565b60606102746102ce565b600061027e610169565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102b46102ce565b600061027e61035c565b6101676102c961035c565b61036b565b341561016757600080fd5b6102e28361038f565b6000825111806102ef5750805b15610300576102fe83836103cf565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61032e610169565b604080516001600160a01b03928316815291841660208301520160405180910390a1610359816103fb565b50565b60006103666104a4565b905090565b3660008037600080366000845af43d6000803e80801561038a573d6000f35b3d6000fd5b610398816104cc565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606103f4838360405180606001604052806027815260200161083060279139610560565b9392505050565b6001600160a01b0381166104605760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61018d565b6001600160a01b0381163b6105395760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610483565b6060600080856001600160a01b03168560405161057d91906107e0565b600060405180830381855af49150503d80600081146105b8576040519150601f19603f3d011682016040523d82523d6000602084013e6105bd565b606091505b50915091506105ce868383876105d8565b9695505050505050565b60608315610647578251600003610640576001600160a01b0385163b6106405760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b5081610651565b6106518383610659565b949350505050565b8151156106695781518083602001fd5b8060405162461bcd60e51b815260040161014e91906107fc565b6000808585111561069357600080fd5b838611156106a057600080fd5b5050820193919092039150565b80356001600160a01b03811681146106c457600080fd5b919050565b6000602082840312156106db57600080fd5b6103f4826106ad565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561070d57600080fd5b610716836106ad565b9150602083013567ffffffffffffffff8082111561073357600080fd5b818501915085601f83011261074757600080fd5b813581811115610759576107596106e4565b604051601f8201601f19908116603f01168101908382118183101715610781576107816106e4565b8160405282815288602084870101111561079a57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107d75781810151838201526020016107bf565b50506000910152565b600082516107f28184602087016107bc565b9190910192915050565b602081526000825180602084015261081b8160408501602087016107bc565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220b22984eb1f3348f5b2148862b6f80392e497e3c65d0d2cfbb5e53d737e5a6c6a64736f6c63430008190033", "storage": { "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x0000000000000000000000000c0deba5e0000000000000000000000000000000", "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000c0ffee1234567890abcdef1234567890abcdef34" }, "balance": "0x0", "nonce": "0x1" }, "32aaa04b1c166d02b0ee152dd221367687f72108": { "balance": "0x2086ac351052600000" }, "48a90c916ad48a72f49fa72a9f889c1ba9cc9b4b": { "balance": "0x8ac7230489e80000" }, "8db97c7cece249c2b98bdc0226cc4c2a57bf52fc": { "balance": "0xd3c21bcecceda1000000" }, "c0ffee1234567890abcdef1234567890abcdef34": { "code": "0x60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461011157806399a88ec414610124578063f2fde38b14610144578063f3b7dead1461016457600080fd5b8063204e1c7a14610080578063715018a6146100bc5780637eff275e146100d35780638da5cb5b146100f3575b600080fd5b34801561008c57600080fd5b506100a061009b366004610499565b610184565b6040516001600160a01b03909116815260200160405180910390f35b3480156100c857600080fd5b506100d1610215565b005b3480156100df57600080fd5b506100d16100ee3660046104bd565b610229565b3480156100ff57600080fd5b506000546001600160a01b03166100a0565b6100d161011f36600461050c565b610291565b34801561013057600080fd5b506100d161013f3660046104bd565b610300565b34801561015057600080fd5b506100d161015f366004610499565b610336565b34801561017057600080fd5b506100a061017f366004610499565b6103b4565b6000806000836001600160a01b03166040516101aa90635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101e5576040519150601f19603f3d011682016040523d82523d6000602084013e6101ea565b606091505b5091509150816101f957600080fd5b8080602001905181019061020d91906105e2565b949350505050565b61021d6103da565b6102276000610434565b565b6102316103da565b6040516308f2839760e41b81526001600160a01b038281166004830152831690638f283970906024015b600060405180830381600087803b15801561027557600080fd5b505af1158015610289573d6000803e3d6000fd5b505050505050565b6102996103da565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906102c990869086906004016105ff565b6000604051808303818588803b1580156102e257600080fd5b505af11580156102f6573d6000803e3d6000fd5b5050505050505050565b6103086103da565b604051631b2ce7f360e11b81526001600160a01b038281166004830152831690633659cfe69060240161025b565b61033e6103da565b6001600160a01b0381166103a85760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b6103b181610434565b50565b6000806000836001600160a01b03166040516101aa906303e1469160e61b815260040190565b6000546001600160a01b031633146102275760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161039f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146103b157600080fd5b6000602082840312156104ab57600080fd5b81356104b681610484565b9392505050565b600080604083850312156104d057600080fd5b82356104db81610484565b915060208301356104eb81610484565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561052157600080fd5b833561052c81610484565b9250602084013561053c81610484565b9150604084013567ffffffffffffffff8082111561055957600080fd5b818601915086601f83011261056d57600080fd5b81358181111561057f5761057f6104f6565b604051601f8201601f19908116603f011681019083821181831017156105a7576105a76104f6565b816040528281528960208487010111156105c057600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156105f457600080fd5b81516104b681610484565b60018060a01b03831681526000602060406020840152835180604085015260005b8181101561063c57858101830151858201606001528201610620565b506000606082860101526060601f19601f83011685010192505050939250505056fea264697066735822122019f39983a6fd15f3cffa764efd6fb0234ffe8d71051b3ebddc0b6bd99f87fa9764736f6c63430008190033", "storage": { "0x0000000000000000000000000000000000000000000000000000000000000000": "0x00000000000000000000000048a90c916ad48a72f49fa72a9f889c1ba9cc9b4b" }, "balance": "0x0", "nonce": "0x1" } }, "airdropHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "airdropAmount": null, "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "baseFeePerGas": null, "excessBlobGas": null, "blobGasUsed": null } ``` Open a text editor and copy the preceding text into a file called `custom_genesis.json`. For full breakdown of the genesis file, see the [Genesis File](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#genesis). The `timestamp` field is the Unix timestamp of the genesis block. `0x677eb2d9` represents the timestamp 1736356569 which is the time this tutorial was written. You should use the timestamp when you create your genesis file. Create the Avalanche L1 Configuration[​](#create-the-avalanche-l1-configuration "Direct link to heading") --------------------------------------------------------------------------------------------- Now that you have your binary, it's time to create the Avalanche L1 configuration. This tutorial uses `myblockchain` as it's Avalanche L1 name. Invoke the Avalanche L1 Creation Wizard with this command: ```bash avalanche blockchain create myblockchain ``` ### Choose Your VM[​](#choose-your-vm "Direct link to heading") Select `Custom` for your VM. ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose your VM: Subnet-EVM ▸ Custom ``` ### Select Validator Manager type ```bash Which validator management type would you like to use in your blockchain?: ▸ Proof Of Authority Proof Of Stake Explain the difference ``` Select the Validator manager that matches the `deployedBytecode` in your genesis. This tutorial is using Proof Of Authority. Next, select the key that will be used to controller the PoA ValidatorManager contract: ```bash Which address do you want to enable as controller of ValidatorManager contract?: ▸ Get address from an existing stored key (created from avalanche key create or avalanche key import) Custom ``` This key can add, remove and change weight of the validator set. ### Enter the Path to Your Genesis[​](#enter-the-path-to-your-genesis "Direct link to heading") Enter the path to the genesis file you created in this [step](#create-a-custom-genesis). ```bash ✔ Enter path to custom genesis: ./custom_genesis.json ``` ### ICM Setup ```bash ? Do you want to connect your blockchain with other blockchains or the C-Chain?: Yes, I want to enable my blockchain to interoperate with other blockchains and the C-Chain ▸ No, I want to run my blockchain isolated Explain the difference ``` Select no for now as we can setup ICM after deployment. ### Enter the Path to Your VM Binary[​](#enter-the-path-to-your-vm-binary "Direct link to heading") ```bash ? How do you want to set up the VM binary?: Download and build from a git repository (recommended for cloud deployments) ▸ I already have a VM binary (local network deployments only) ``` Select `I already have a VM binary`. Next, enter the path to your VM binary. This should be the path to the `custom_evm.bin` you created [previously](#modify-and-build-subnet-evm). ```bash ✔ Enter path to vm binary: ./custom_vm.bin ``` ### Wrapping Up[​](#wrapping-up "Direct link to heading") If all worked successfully, the command prints: ```bash ✓ Successfully created blockchain configuration Run 'avalanche blockchain describe' to view all created addresses and what their roles are ``` Now it's time to deploy it. Deploy the Avalanche L1 Locally[​](#deploy-the-avalanche-l1-locally "Direct link to heading") --------------------------------------------------------------------------------- To deploy your Avalanche L1, run: ```bash avalanche blockchain deploy myblockchain ``` Make sure to substitute the name of your Avalanche L1 if you used a different one than `myblockchain`. Next, select `Local Network`: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to deploy on: ▸ Local Network Fuji Mainnet ``` This command boots a five node Avalanche network on your machine. It needs to download the latest versions of AvalancheGo and Subnet-EVM. The command may take a couple minutes to run. If all works as expected, the command output should look something like this: ```bash Deploying [myblockchain] to Local Network Backend controller started, pid: 49158, output at: /Users/l1-dev/.avalanche-cli/runs/server_20250108_140532/avalanche-cli-backend.log Installing avalanchego-v1.12.1... avalanchego-v1.12.1 installation successful AvalancheGo path: /Users/l1-dev/.avalanche-cli/bin/avalanchego/avalanchego-v1.12.1/avalanchego Booting Network. Wait until healthy... Node logs directory: /Users/l1-dev/.avalanche-cli/runs/network_20250108_140538/node/logs Network ready to use. Your blockchain control keys: [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] Your subnet auth keys for chain creation: [P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p] CreateSubnetTx fee: 0.000010278 AVAX Subnet has been created with ID: GEieSy2doZ96bpMo5CuHPaX1LvaxpKZ9C72L22j94t6YyUb6X Now creating blockchain... CreateChainTx fee: 0.000095896 AVAX +-------------------------------------------------------------------+ | DEPLOYMENT RESULTS | +---------------+---------------------------------------------------+ | Chain Name | myblockchain | +---------------+---------------------------------------------------+ | Subnet ID | GEieSy2doZ96bpMo5CuHPaX1LvaxpKZ9C72L22j94t6YyUb6X | +---------------+---------------------------------------------------+ | VM ID | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | +---------------+---------------------------------------------------+ | Blockchain ID | 9FrNVEPkVpQyWDECQhEPDuT9oK98EhWQdg7anypKujVt9uSVT | +---------------+ | | P-Chain TXID | | +---------------+---------------------------------------------------+ Restarting node node2 to track subnet Restarting node node1 to track subnet Waiting for http://127.0.0.1:9652/ext/bc/9FrNVEPkVpQyWDECQhEPDuT9oK98EhWQdg7anypKujVt9uSVT/rpc to be available Waiting for http://127.0.0.1:9650/ext/bc/9FrNVEPkVpQyWDECQhEPDuT9oK98EhWQdg7anypKujVt9uSVT/rpc to be available ✓ Local Network successfully tracking myblockchain ✓ Subnet is successfully deployed Local Network ``` Use the describe command to find your L1s RPC: ```bash avalanche blockchain describe myblockchain ``` You can use the `RPC URL` to connect to and interact with your Avalanche L1. Interact with Your Avalanche L1[​](#interact-with-your-avalanche-l1 "Direct link to heading") --------------------------------------------------------------------------------- ### Check the Version[​](#check-the-version "Direct link to heading") You can verify that your Avalanche L1 has deployed correctly by querying the local node to see what Avalanche L1s it's running. You need to use the [`getNodeVersion`](/docs/rpcs/other/info-rpc#infogetnodeversion) endpoint. Try running this curl command: ```bash curl --location --request POST 'http://127.0.0.1:9650/ext/info' \ --header 'Content-Type: application/json' \ --data-raw '{ "jsonrpc":"2.0", "id" :1, "method" :"info.getNodeVersion", "params" :{ } }' ``` The command returns a list of all the VMs your local node is currently running along with their versions. ```json { "jsonrpc": "2.0", "result": { "version": "avalanche/1.10.8", "databaseVersion": "v1.4.5", "rpcProtocolVersion": "27", "gitCommit": "e70a17d9d988b5067f3ef5c4a057f15ae1271ac4", "vmVersions": { "avm": "v1.10.8", "evm": "v0.12.5", "platform": "v1.10.8", "qDMnZ895HKpRXA2wEvujJew8nNFEkvcrH5frCR9T1Suk1sREe": "v0.5.4@c0fe6506a40da466285f37dd0d3c044f494cce32" } }, "id": 1 } ``` Your results may be slightly different, but you can see that in addition to the X-Chain's `avm`, the C-Chain's `evm`, and the P-Chain's `platform` VM, the node is running the custom VM with commit `c0fe6506a40da466285f37dd0d3c044f494cce32`. ### Check a Balance[​](#check-a-balance "Direct link to heading") If you used the default genesis, your custom VM has a prefunded address. You can verify its balance with a curl command. Make sure to substitute the command's URL with the `RPC URL` from your deployment output. ```bash curl --location --request POST 'http://127.0.0.1:9650/ext/bc/myblockchain/rpc' \ --header 'Content-Type: application/json' \ --data-raw '{ "jsonrpc": "2.0", "method": "eth_getBalance", "params": [ "0x8db97c7cece249c2b98bdc0226cc4c2a57bf52fc", "latest" ], "id": 1 }' ``` The command should return: ```json { "jsonrpc": "2.0", "id": 1, "result": "0xd3c21bcecceda1000000" } ``` The balance is hex encoded, so this means the address has a balance of 1 million tokens. Note, this command doesn't work on all custom VMs, only VMs that implement the EVM's `eth_getBalance` interface. Next Steps[​](#next-steps "Direct link to heading") --------------------------------------------------- You've now unlocked the ability to deploy custom VMs. Go build something cool! # With Multisig Auth (/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-with-multisig-auth) --- title: With Multisig Auth description: Learn how to create an Avalanche L1 with a multisig authorization. --- Avalanche L1 creators can control critical Avalanche L1 operations with a N of M multisig. This multisig must be setup at deployment time and can't be edited afterward. Multisigs can are available on both the Fuji Testnet and Mainnet. To setup your multisig, you need to know the P-Chain address of each key holder and what you want your signing threshold to be. Avalanche-CLI requires Ledgers for Mainnet deployments. This how-to guide assumes the use of Ledgers for setting up your multisig. ## Prerequisites - [`Avalanche-CLI`](https://github.com/ava-labs/avalanche-cli) installed - Familiarity with process of [Deploying an Avalanche L1 on Testnet](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-fuji-testnet) and [Deploying a Permissioned Avalanche L1 on Mainnet](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-mainnet) - Multiple Ledger devices [configured for Avalanche](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-mainnet#setting-up-your-ledger) - an Avalanche L1 configuration ready to deploy to either Fuji Testnet or Mainnet Getting Started[​](#getting-started "Direct link to heading") ------------------------------------------------------------- When issuing the transactions to create the Avalanche L1, you need to sign the TXs with multiple keys from the multisig. ### Specify Network[​](#specify-network "Direct link to heading") Start the Avalanche L1 deployment with ```bash avalanche blockchain deploy testAvalanche L1 ``` First step is to specify `Fuji` or `Mainnet` as the network: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to deploy on: Local Network Fuji ▸ Mainnet ``` ```bash Deploying [testblockchain] to Mainnet ``` Ledger is automatically recognized as the signature mechanism on `Mainnet`. After that, the CLI shows the first `Mainnet` Ledger address. ```bash Ledger address: P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 ``` ### Set Control Keys[​](#set-control-keys "Direct link to heading") Next the CLI asks the user to specify the control keys. This is where you setup your multisig. ```bash Configure which addresses may make changes to the Avalanche L1. These addresses are known as your control keys. You are going to also set how many control keys are required to make an Avalanche L1 change (the threshold). Use the arrow keys to navigate: ↓ ↑ → ← ? How would you like to set your control keys?: ▸ Use ledger address Custom list ``` Select `Custom list` and add every address that you'd like to be a key holder on the multisig. ```bash ✔ Custom list ? Enter control keys: ▸ Add Delete Preview More Info ↓ Done ``` Use the given menu to add each key, and select `Done` when finished. The output at this point should look something like: ```bash ✔ Custom list ✔ Add Enter P-Chain address (Example: P-...): P-avax1wryu62weky9qjlp40cpmnqf6ml2hytnagj5q28 ✔ Add Enter P-Chain address (Example: P-...): P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 ✔ Add Enter P-Chain address (Example: P-...): P-avax12gcy0xl0al6gcjrt0395xqlcuq078ml93wl5h8 ✔ Add Enter P-Chain address (Example: P-...): P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af ✔ Add Enter P-Chain address (Example: P-...): P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g ✔ Done Your Avalanche L1's control keys: [P-avax1wryu62weky9qjlp40cpmnqf6ml2hytnagj5q28 P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 P-avax12gcy0xl0al6gcjrt0395xqlcuq078ml93wl5h8 P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g] ``` When deploying an Avalanche L1 with Ledger, you must include the Ledger's default address determined in [Specify Network](#specify-network) for the deployment to succeed. You may see an error like ``` Error: wallet does not contain Avalanche L1 auth keys exit status 1 ``` ### Set Threshold[​](#set-threshold "Direct link to heading") Next, specify the threshold. In your N of M multisig, your threshold is N, and M is the number of control keys you added in the previous step. ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Select required number of control key signatures to make an Avalanche L1 change: ▸ 1 2 3 4 5 ``` ### Specify Control Keys to Sign the Chain Creation TX[​](#specify-control-keys-to-sign-the-chain-creation-tx "Direct link to heading") You now need N of your key holders to sign the Avalanche L1 deployment transaction. You must select which addresses you want to sign the TX. ```bash ? Choose an Avalanche L1 auth key: ▸ P-avax1wryu62weky9qjlp40cpmnqf6ml2hytnagj5q28 P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 P-avax12gcy0xl0al6gcjrt0395xqlcuq078ml93wl5h8 P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g ``` A successful control key selection looks like: ```bash ✔ 2 ✔ P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 ✔ P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af Your subnet auth keys for chain creation: [P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af] *** Please sign Avalanche L1 creation hash on the ledger device *** ``` #### Potential Errors[​](#potential-errors "Direct link to heading") If the currently connected Ledger address isn't included in your TX signing group, the operation fails with: ```bash ✔ 2 ✔ P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af ✔ P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g Your Avalanche L1 auth keys for chain creation: [P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g] Error: wallet does not contain Avalanche L1 auth keys exit status 1 ``` This can happen either because the original specified control keys -previous step- don't contain the Ledger address, or because the Ledger address control key wasn't selected in the current step. If the user has the correct address but doesn't have sufficient balance to pay for the TX, the operation fails with: ```bash ✔ 2 ✔ P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af ✔ P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g Your Avalanche L1 auth keys for chain creation: [P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g] *** Please sign Avalanche L1 creation hash on the ledger device *** Error: insufficient funds: provided UTXOs need 1000000000 more units of asset "rgNLkDPpANwqg3pHC4o9aGJmf2YU4GgTVUMRKAdnKodihkqgr" exit status 1 ``` ### Sign Avalanche L1 Deployment TX with the First Address[​](#sign-avalanche-l1-deployment-tx-with-the-first-address "Direct link to heading") The Avalanche L1 Deployment TX is ready for signing. ```bash *** Please sign Avalanche L1 creation hash on the ledger device *** ``` This activates a `Please review` window on the Ledger. Navigate to the Ledger's `APPROVE` window by using the Ledger's right button, and then authorize the request by pressing both left and right buttons. ```bash Avalanche L1 has been created with ID: 2qUKjvPx68Fgc1NMi8w4mtaBt5hStgBzPhsQrS1m7vSub2q9ew. Now creating blockchain... *** Please sign blockchain creation hash on the ledger device *** ``` After successful Avalanche L1 creation, the CLI asks the user to sign the blockchain creation TX. This activates a `Please review` window on the Ledger. Navigate to the Ledger's `APPROVE` window by using the Ledger's right button, and then authorize the request by pressing both left and right buttons. On success, the CLI provides Avalanche L1 deploy details. As only one address signed the chain creation TX, the CLI writes a file to disk to save the TX to continue the signing process with another command. ```bash +--------------------+----------------------------------------------------+ | DEPLOYMENT RESULTS | | +--------------------+----------------------------------------------------+ | Chain Name | testblockchain | +--------------------+----------------------------------------------------+ | Subnet ID | 2qUKjvPx68Fgc1NMi8w4mtaBt5hStgBzPhsQrS1m7vSub2q9ew | +--------------------+----------------------------------------------------+ | VM ID | rW1esjm6gy4BtGvxKMpHB2M28MJGFNsqHRY9AmnchdcgeB3ii | +--------------------+----------------------------------------------------+ 1 of 2 required Blockchain Creation signatures have been signed. Saving TX to disk to enable remaining signing. Path to export partially signed TX to: ``` Enter the name of file to write to disk, such as `partiallySigned.txt`. This file shouldn't exist already. ```bash Path to export partially signed TX to: partiallySigned.txt Addresses remaining to sign the tx: P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af Connect a ledger with one of the remaining addresses or choose a stored key and run the signing command, or send "partiallySigned.txt" to another user for signing. Signing command: avalanche transaction sign testblockchain --input-tx-filepath partiallySigned.txt ``` Gather Remaining Signatures and Issue the Avalanche L1 Deployment TX[​](#gather-remaining-signatures-and-issue-the-avalanche-l1-deployment-tx "Direct link to heading") ----------------------------------------------------------------------------------------------------------------------------------------------------------- So far, one address has signed the Avalanche L1 deployment TX, but you need N signatures. Your Avalanche L1 has not been fully deployed yet. To get the remaining signatures, you may connect a different Ledger to the same computer you've been working on. Alternatively, you may send the `partiallySigned.txt` file to other users to sign themselves. The remainder of this section assumes that you are working on a machine with access to both the remaining keys and the `partiallySigned.txt` file. ### Issue the Command to Sign the Chain Creation TX[​](#issue-the-command-to-sign-the-chain-creation-tx "Direct link to heading") Avalanche-CLI can detect the deployment network automatically. For `Mainnet` TXs, it uses your Ledger automatically. For `Fuji Testnet`, the CLI prompts the user to choose the signing mechanism. You can start the signing process with the `transaction sign` command: ```bash avalanche transaction sign testblockchain --input-tx-filepath partiallySigned.txt ``` ```bash Ledger address: P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af *** Please sign TX hash on the ledger device *** ``` Next, the CLI starts a new signing process for the Avalanche L1 deployment TX. If the Ledger isn't the correct one, the following error should appear instead: ```bash Ledger address: P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 Error: wallet does not contain Avalanche L1 auth keys exit status 1 ``` This activates a `Please review` window on the Ledger. Navigate to the Ledger's `APPROVE` window by using the Ledger's right button, and then authorize the request by pressing both left and right buttons. Repeat this processes until all required parties have signed the TX. You should see a message like this: ```bash All 2 required Tx signatures have been signed. Saving TX to disk to enable commit. Overwriting partiallySigned.txt Tx is fully signed, and ready to be committed Commit command: avalanche transaction commit testblockchain --input-tx-filepath partiallySigned.txt ``` Now, `partiallySigned.txt` contains a fully signed TX. ### Commit the Avalanche L1 Deployment TX[​](#commit-the-avalanche-l1-deployment-tx "Direct link to heading") To run submit the fully signed TX, run: ```bash avalanche transaction commit testblockchain --input-tx-filepath partiallySigned.txt ``` The CLI recognizes the deployment network automatically and submits the TX appropriately. ```bash +--------------------+-------------------------------------------------------------------------------------+ | DEPLOYMENT RESULTS | | +--------------------+-------------------------------------------------------------------------------------+ | Chain Name | testblockchain | +--------------------+-------------------------------------------------------------------------------------+ | Subnet ID | 2qUKjvPx68Fgc1NMi8w4mtaBt5hStgBzPhsQrS1m7vSub2q9ew | +--------------------+-------------------------------------------------------------------------------------+ | VM ID | rW1esjm6gy4BtGvxKMpHB2M28MJGFNsqHRY9AmnchdcgeB3ii | +--------------------+-------------------------------------------------------------------------------------+ | Blockchain ID | 2fx9EF61C964cWBu55vcz9b7gH9LFBkPwoj49JTSHA6Soqqzoj | +--------------------+-------------------------------------------------------------------------------------+ | RPC URL | http://127.0.0.1:9650/ext/bc/2fx9EF61C964cWBu55vcz9b7gH9LFBkPwoj49JTSHA6Soqqzoj/rpc | +--------------------+-------------------------------------------------------------------------------------+ | P-Chain TXID | 2fx9EF61C964cWBu55vcz9b7gH9LFBkPwoj49JTSHA6Soqqzoj | +--------------------+-------------------------------------------------------------------------------------+ ``` Your Avalanche L1 successfully deployed with a multisig. Add Validators Using the Multisig[​](#add-validators-using-the-multisig "Direct link to heading") ------------------------------------------------------------------------------------------------- The `addValidator` command also requires use of the multisig. Before starting, make sure to connect, unlock, and run the Avalanche Ledger app. ```bash avalanche blockchain addValidator testblockchain ``` ### Select Network[​](#select-network "Direct link to heading") First specify the network. Select either `Fuji` or `Mainnet`: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to add validator to.: ▸ Fuji Mainnet ``` ### Choose Signing Keys[​](#choose-signing-keys "Direct link to heading") Then, similar to the `deploy` command, the command asks the user to select the N control keys needed to sign the TX. ```bash ✔ Mainnet Use the arrow keys to navigate: ↓ ↑ → ← ? Choose an Avalanche L1 auth key: ▸ P-avax1wryu62weky9qjlp40cpmnqf6ml2hytnagj5q28 P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 P-avax12gcy0xl0al6gcjrt0395xqlcuq078ml93wl5h8 P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af P-avax1g4eryh40dtcsltmxn9zk925ny07gdq2xyjtf4g ``` ```bash ✔ Mainnet ✔ P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 ✔ P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af Your subnet auth keys for add validator TX creation: [P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af]. ``` ### Finish Assembling the TX[​](#finish-assembling-the-tx "Direct link to heading") Take a look at [Add a Validator](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-mainnet#add-a-validator) for additional help issuing this transaction. If setting up a multisig, don't select your validator start time to be in one minute. Finishing the signing process takes significantly longer when using a multisig. ```bash Next, we need the NodeID of the validator you want to whitelist. Check https://build.avax.network/docs/rpcs/other/info-rpc#info-getnodeid for instructions about how to query the NodeID from your node (Edit host IP address and port to match your deployment, if needed). What is the NodeID of the validator you'd like to whitelist?: NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg ✔ Default (20) When should your validator start validating? If your validator is not ready by this time, Avalanche L1 downtime can occur. ✔ Custom When should the validator start validating? Enter a UTC datetime in 'YYYY-MM-DD HH:MM:SS' format: 2022-11-22 23:00:00 ✔ Until primary network validator expires NodeID: NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg Network: Local Network Start time: 2022-11-22 23:00:00 End time: 2023-11-22 15:57:27 Weight: 20 Inputs complete, issuing transaction to add the provided validator information... ``` ```bash Ledger address: P-avax1kdzq569g2c9urm9887cmldlsa3w3jhxe0knfy5 *** Please sign add validator hash on the ledger device *** ``` After that, the command shows the connected Ledger's address, and asks the user to sign the TX with the Ledger. ```bash Partial TX created 1 of 2 required Add Validator signatures have been signed. Saving TX to disk to enable remaining signing. Path to export partially signed TX to: ``` Because you've setup a multisig, TX isn't fully signed, and the commands asks a file to write into. Use something like `partialAddValidatorTx.txt`. ```bash Path to export partially signed TX to: partialAddValidatorTx.txt Addresses remaining to sign the tx: P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af Connect a Ledger with one of the remaining addresses or choose a stored key and run the signing command, or send "partialAddValidatorTx.txt" to another user for signing. Signing command: avalanche transaction sign testblockchain --input-tx-filepath partialAddValidatorTx.txt ``` Sign and Commit the Add Validator TX[​](#sign-and-commit-the-add-validator-tx "Direct link to heading") ------------------------------------------------------------------------------------------------------- The process is very similar to signing of Avalanche L1 Deployment TX. So far, one address has signed the TX, but you need N signatures. To get the remaining signatures, you may connect a different Ledger to the same computer you've been working on. Alternatively, you may send the `partialAddValidatorTx.txt` file to other users to sign themselves. The remainder of this section assumes that you are working on a machine with access to both the remaining keys and the `partialAddValidatorTx.txt` file. ### Issue the Command to Sign the Add Validator TX[​](#issue-the-command-to-sign-the-add-validator-tx "Direct link to heading") Avalanche-CLI can detect the deployment network automatically. For `Mainnet` TXs, it uses your Ledger automatically. For `Fuji Testnet`, the CLI prompts the user to choose the signing mechanism. ```bash avalanche transaction sign testblockchain --input-tx-filepath partialAddValidatorTx.txt ``` ```bash Ledger address: P-avax1g7nkguzg8yju8cq3ndzc9lql2yg69s9ejqa2af *** Please sign TX hash on the ledger device *** ``` Next, the command is going to start a new signing process for the Add Validator TX. This activates a `Please review` window on the Ledger. Navigate to the Ledger's `APPROVE` window by using the Ledger's right button, and then authorize the request by pressing both left and right buttons. Repeat this processes until all required parties have signed the TX. You should see a message like this: ```bash All 2 required Tx signatures have been signed. Saving TX to disk to enable commit. Overwriting partialAddValidatorTx.txt Tx is fully signed, and ready to be committed Commit command: avalanche transaction commit testblockchain --input-tx-filepath partialAddValidatorTx.txt ``` Now, `partialAddValidatorTx.txt` contains a fully signed TX. ### Issue the Command to Commit the add validator TX[​](#issue-the-command-to-commit-the-add-validator-tx "Direct link to heading") To run submit the fully signed TX, run: ```bash avalanche transaction commit testblockchain --input-tx-filepath partialAddValidatorTx.txt ``` The CLI recognizes the deployment network automatically and submits the TX appropriately. ```bash Transaction successful, transaction ID: K7XNSwcmgjYX7BEdtFB3hEwQc6YFKRq9g7hAUPhW4J5bjhEJG ``` You've successfully added the validator to the Avalanche L1. # Teleporter on Devnet (/docs/tooling/avalanche-cli/cross-chain/teleporter-devnet) --- title: Teleporter on Devnet description: This how-to guide focuses on deploying Teleporter-enabled Avalanche L1s to a Devnet. --- After this tutorial, you would have created a Devnet and deployed two Avalanche L1s in it, and have enabled them to cross-communicate with each other and with the C-Chain through Teleporter and the underlying Warp technology. For more information on cross chain messaging through Teleporter and Warp, check: - [Cross Chain References](/docs/cross-chain) Note that currently only [Subnet-EVM](https://github.com/ava-labs/subnet-evm) and [Subnet-EVM-Based](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1) virtual machines support Teleporter. ## Prerequisites Before we begin, you will need to have: - Created an AWS account and have an updated AWS `credentials` file in home directory with \[default\] profile Note: the tutorial uses AWS hosts, but Devnets can also be created and operated in other supported cloud providers, such as GCP. Create Avalanche L1s Configurations[​](#create-avalanche-l1s-configurations "Direct link to heading") ----------------------------------------------------------------------------------------- For this section we will follow this [steps](/docs/tooling/avalanche-cli/cross-chain/teleporter-local-network#create-avalanche-l1s-configurations), to create two Teleporter-enabled Avalanche L1s, `` and ``. Create a Devnet and Deploy an Avalanche L1 in It[​](#create-a-devnet-and-deploy-a-avalanche-l1-in-it "Direct link to heading") ----------------------------------------------------------------------------------------------------------------- Let's use the `devnet wiz` command to create a devnet `` and deploy `` in it. The devnet will be created in the `us-east-1` region of AWS, and will consist of 5 validators only. ```bash avalanche node devnet wiz --aws --node-type default --region us-east-1 --num-validators 5 --num-apis 0 --enable-monitoring=false --default-validator-params Creating the devnet... Creating new EC2 instance(s) on AWS... ... Deploying [chain1] to Cluster ... configuring AWM RElayer on host i-0f1815c016b555fcc Setting the nodes as subnet trackers ... Setting up teleporter on subnet Teleporter Messenger successfully deployed to chain1 (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) Teleporter Registry successfully deployed to chain1 (0xb623C4495220C603D0A939D32478F55891a61750) Teleporter Messenger successfully deployed to c-chain (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) Teleporter Registry successfully deployed to c-chain (0x5DB9A7629912EBF95876228C24A848de0bfB43A9) Starting AWM Relayer Service setting AWM Relayer on host i-0f1815c016b555fcc to relay subnet chain1 updating configuration file ~/.avalanche-cli/nodes/i-0f1815c016b555fcc/services/awm-relayer/awm-relayer-config.json Devnet is successfully created and is now validating subnet chain1! Subnet RPC URL: http://67.202.23.231:9650/ext/bc/fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p/rpc ✓ Cluster information YAML file can be found at ~/.avalanche-cli/nodes/inventories//clusterInfo.yaml at local host ``` Notice some details here: - Two smart contracts are deployed to the Avalanche L1: Teleporter Messenger and Teleporter Registry - Both Teleporter smart contracts are also deployed to `C-Chain` - [AWM Teleporter Relayer](https://github.com/ava-labs/awm-relayer) is installed and configured as a service into one of the nodes (A Relayer [listens](/docs/cross-chain/teleporter/overview#data-flow) for new messages being generated on a source Avalanche L1 and sends them to the destination Avalanche L1.) CLI configures the Relayer to enable every Avalanche L1 to send messages to all other Avalanche L1s. If you add more Avalanche L1s to the Devnet, the Relayer will be automatically reconfigured. Checking Devnet Configuration and Relayer Logs[​](#checking-devnet-configuration-and-relayer-logs "Direct link to heading") --------------------------------------------------------------------------------------------------------------------------- Execute `node list` command to get a list of the devnet nodes: ```bash avalanche node list Cluster "" (Devnet) Node i-0f1815c016b555fcc (NodeID-91PGQ7keavfSV1XVFva2WsQXWLWZqqqKe) 67.202.23.231 [Validator,Relayer] Node i-026392a651571232c (NodeID-AkPyyTs9e9nPGShdSoxdvWYZ6X2zYoyrK) 52.203.183.68 [Validator] Node i-0d1b98d5d941d6002 (NodeID-ByEe7kuwtrPStmdMgY1JiD39pBAuFY2mS) 50.16.235.194 [Validator] Node i-0c291f54bb38c2984 (NodeID-8SE2CdZJExwcS14PYEqr3VkxFyfDHKxKq) 52.45.0.56 [Validator] Node i-049916e2f35231c29 (NodeID-PjQY7xhCGaB8rYbkXYddrr1mesYi29oFo) 3.214.163.110 [Validator] ``` Notice that, in this case, `i-0f1815c016b555fcc` was set as Relayer. This host contains a `systemd` service called `awm-relayer` that can be used to check the Relayer logs, and set the execution status. To view the Relayer logs, the following command can be used: ```bash avalanche node ssh i-0f1815c016b555fcc "journalctl -u awm-relayer --no-pager" [Node i-0f1815c016b555fcc (NodeID-91PGQ7keavfSV1XVFva2WsQXWLWZqqqKe) 67.202.23.231 [Validator,Relayer]] Warning: Permanently added '67.202.23.231' (ED25519) to the list of known hosts. -- Logs begin at Fri 2024-04-05 14:11:43 UTC, end at Fri 2024-04-05 14:30:24 UTC. -- Apr 05 14:15:06 ip-172-31-47-187 systemd[1]: Started AWM Relayer systemd service. Apr 05 14:15:07 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:07.018Z","logger":"awm-relayer","caller":"main/main.go:66","msg":"Initializing awm-relayer"} Apr 05 14:15:07 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:07.018Z","logger":"awm-relayer","caller":"main/main.go:71","msg":"Set config options."} Apr 05 14:15:07 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:07.018Z","logger":"awm-relayer","caller":"main/main.go:78","msg":"Initializing destination clients"} Apr 05 14:15:07 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:07.021Z","logger":"awm-relayer","caller":"main/main.go:97","msg":"Initializing app request network"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.159Z","logger":"awm-relayer","caller":"main/main.go:309","msg":"starting metrics server...","port":9090} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.160Z","logger":"awm-relayer","caller":"main/main.go:251","msg":"Creating relayer","originBlockchainID":"fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.160Z","logger":"awm-relayer","caller":"main/main.go:251","msg":"Creating relayer","originBlockchainID":"2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.160Z","logger":"awm-relayer","caller":"relayer/relayer.go:114","msg":"Creating relayer","subnetID":"11111111111111111111111111111111LpoYY","subnetIDHex":"0000000000000000000000000000000000000000000000000000000000000000","blockchainID":"2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6","blockchainIDHex":"a2b6b947cf2b9bf6df03c8caab08e38ab951d8b120b9c37265d9be01d86bb170"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.160Z","logger":"awm-relayer","caller":"relayer/relayer.go:114","msg":"Creating relayer","subnetID":"giY8tswWgZmcAWzPkoNrmjjrykited7GJ9799SsFzTiq5a1ML","subnetIDHex":"5a2e2d87d74b4ec62fdd6626e7d36a44716484dfcc721aa4f2168e8a61af63af","blockchainID":"fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p","blockchainIDHex":"582fc7bd55472606c260668213bf1b6d291df776c9edf7e042980a84cce7418a"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.171Z","logger":"awm-relayer","caller":"evm/subscriber.go:247","msg":"Successfully subscribed","blockchainID":"2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.171Z","logger":"awm-relayer","caller":"relayer/relayer.go:161","msg":"processed-missed-blocks set to false, starting processing from chain head","blockchainID":"2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.172Z","logger":"awm-relayer","caller":"relayer/message_relayer.go:662","msg":"Updating latest processed block in database","relayerID":"0xea06381426934ec1800992f41615b9d362c727ad542f6351dbfa7ad2849a35bf","latestBlock":6} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.173Z","logger":"awm-relayer","caller":"relayer/message_relayer.go:662","msg":"Updating latest processed block in database","relayerID":"0x175e14327136d57fe22d4bdd295ff14bea8a7d7ab1884c06a4d9119b9574b9b3","latestBlock":6} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.173Z","logger":"awm-relayer","caller":"main/main.go:272","msg":"Created relayer","blockchainID":"2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.173Z","logger":"awm-relayer","caller":"main/main.go:295","msg":"Relayer initialized. Listening for messages to relay.","originBlockchainID":"2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.178Z","logger":"awm-relayer","caller":"evm/subscriber.go:247","msg":"Successfully subscribed","blockchainID":"fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.178Z","logger":"awm-relayer","caller":"relayer/relayer.go:161","msg":"processed-missed-blocks set to false, starting processing from chain head","blockchainID":"fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.179Z","logger":"awm-relayer","caller":"relayer/message_relayer.go:662","msg":"Updating latest processed block in database","relayerID":"0xe584ccc0df44506255811f6b54375e46abd5db40a4c84fd9235a68f7b69c6f06","latestBlock":6} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.179Z","logger":"awm-relayer","caller":"relayer/message_relayer.go:662","msg":"Updating latest processed block in database","relayerID":"0x70f14d33bde4716928c5c4723d3969942f9dfd1f282b64ffdf96f5ac65403814","latestBlock":6} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.180Z","logger":"awm-relayer","caller":"main/main.go:272","msg":"Created relayer","blockchainID":"fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p"} Apr 05 14:15:08 ip-172-31-47-187 awm-relayer[6886]: {"level":"info","timestamp":"2024-04-05T14:15:08.180Z","logger":"awm-relayer","caller":"main/main.go:295","msg":"Relayer initialized. Listening for messages to relay.","originBlockchainID":"fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p"} ``` Deploying the Second Avalanche L1[​](#deploying-the-second-avalanche-l1 "Direct link to heading") ------------------------------------------------------------------------------------- Let's use the `devnet wiz` command again to deploy ``. When deploying Avalanche L1 ``, the two Teleporter contracts will not be deployed to C-Chain in Local Network as they have already been deployed when we deployed the first Avalanche L1. ```bash avalanche node devnet wiz --default-validator-params Adding subnet into existing devnet ... ... Deploying [chain2] to Cluster ... Stopping AWM Relayer Service Setting the nodes as subnet trackers ... Setting up teleporter on subnet Teleporter Messenger successfully deployed to chain2 (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) Teleporter Registry successfully deployed to chain2 (0xb623C4495220C603D0A939D32478F55891a61750) Teleporter Messenger has already been deployed to c-chain Starting AWM Relayer Service setting AWM Relayer on host i-0f1815c016b555fcc to relay subnet chain2 updating configuration file ~/.avalanche-cli/nodes/i-0f1815c016b555fcc/services/awm-relayer/awm-relayer-config.json Devnet is now validating subnet chain2 Subnet RPC URL: http://67.202.23.231:9650/ext/bc/7gKt6evRnkA2uVHRfmk9WrH3dYZH9gEVVxDAknwtjvtaV3XuQ/rpc ✓ Cluster information YAML file can be found at ~/.avalanche-cli/nodes/inventories//clusterInfo.yaml at local host ``` Verify Teleporter Is Successfully Set Up[​](#verify-teleporter-is-successfully-set-up "Direct link to heading") --------------------------------------------------------------------------------------------------------------- To verify that Teleporter is successfully, let's send a couple of cross messages: ```bash avalanche teleporter msg C-Chain chain1 "Hello World" --cluster Delivering message "this is a message" to source subnet "C-Chain" (2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6) Waiting for message to be received at destination subnet subnet "chain1" (fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p) Message successfully Teleported! ``` ```bash avalanche teleporter msg chain2 chain1 "Hello World" --cluster Delivering message "this is a message" to source subnet "chain2" (29WP91AG7MqPUFEW2YwtKnsnzVrRsqcWUpoaoSV1Q9DboXGf4q) Waiting for message to be received at destination subnet subnet "chain1" (fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p) Message successfully Teleported! ``` You have Teleport-ed your first message in the Devnet! Obtaining Information on Teleporter Deploys[​](#obtaining-information-on-teleporter-deploys "Direct link to heading") --------------------------------------------------------------------------------------------------------------------- ### Obtaining Avalanche L1 Information[​](#obtaining-avalanche-l1-information "Direct link to heading") By executing `blockchain describe` on a Teleporter enabled Avalanche L1, the following relevant information can be found: - Blockchain RPC URL - Blockchain ID in cb58 format - Blockchain ID in plain hex format - Teleporter Messenger address - Teleporter Registry address Let's get the information for ``: ```bash avalanche blockchain describe _____ _ _ _ | __ \ | | (_) | | | | | ___| |_ __ _ _| |___ | | | |/ _ \ __/ _ | | / __| | |__| | __/ || (_| | | \__ \ |_____/ \___|\__\__,_|_|_|___/ +--------------------------------+----------------------------------------------------------------------------------------+ | PARAMETER | VALUE | +--------------------------------+----------------------------------------------------------------------------------------+ | Blockchain Name | chain1 | +--------------------------------+----------------------------------------------------------------------------------------+ | ChainID | 1 | +--------------------------------+----------------------------------------------------------------------------------------+ | Token Name | TOKEN1 Token | +--------------------------------+----------------------------------------------------------------------------------------+ | Token Symbol | TOKEN1 | +--------------------------------+----------------------------------------------------------------------------------------+ | VM Version | v0.6.3 | +--------------------------------+----------------------------------------------------------------------------------------+ | VM ID | srEXiWaHjFEgKSgK2zBgnWQUVEy2MZA7UUqjqmBSS7MZYSCQ5 | +--------------------------------+----------------------------------------------------------------------------------------+ | Cluster SubnetID | giY8tswWgZmcAWzPkoNrmjjrykited7GJ9799SsFzTiq5a1ML | +--------------------------------+----------------------------------------------------------------------------------------+ | Cluster RPC URL | http://67.202.23.231:9650/ext/bc/fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p/rpc | +--------------------------------+----------------------------------------------------------------------------------------+ | Cluster | fqcM24LNb3kTV7KD1mAvUJXYy5XunwP8mrE44YuNwPjgZHY6p | | BlockchainID | | + +----------------------------------------------------------------------------------------+ | | 0x582fc7bd55472606c260668213bf1b6d291df776c9edf7e042980a84cce7418a | | | | +--------------------------------+----------------------------------------------------------------------------------------+ | Cluster Teleporter| 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf | | Messenger Address | | +--------------------------------+----------------------------------------------------------------------------------------+ | Cluster Teleporter| 0xb623C4495220C603D0A939D32478F55891a61750 | | Registry Address | | +--------------------------------+----------------------------------------------------------------------------------------+ ... ``` ### Obtaining C-Chain Information[​](#obtaining-c-chain-information "Direct link to heading") Similar information can be found for C-Chain by using `primary describe`: ```bash avalanche primary describe --cluster _____ _____ _ _ _____ / ____| / ____| | (_) | __ \ | | ______| | | |__ __ _ _ _ __ | |__) |_ _ _ __ __ _ _ __ ___ ___ | | |______| | | '_ \ / _ | | '_ \ | ___/ _ | '__/ _ | '_ _ \/ __| | |____ | |____| | | | (_| | | | | | | | | (_| | | | (_| | | | | | \__ \ \_____| \_____|_| |_|\__,_|_|_| |_| |_| \__,_|_| \__,_|_| |_| |_|___/ +------------------------------+--------------------------------------------------------------------+ | PARAMETER | VALUE | +------------------------------+--------------------------------------------------------------------+ | RPC URL | http://67.202.23.231:9650/ext/bc/C/rpc | +------------------------------+--------------------------------------------------------------------+ | EVM Chain ID | 43112 | +------------------------------+--------------------------------------------------------------------+ | TOKEN SYMBOL | AVAX | +------------------------------+--------------------------------------------------------------------+ | Address | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | +------------------------------+--------------------------------------------------------------------+ | Balance | 49999489.815751426 | +------------------------------+--------------------------------------------------------------------+ | Private Key | 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 | +------------------------------+--------------------------------------------------------------------+ | BlockchainID | 2EfJg86if9Ka5Ag73JRfoqWz4EGuFwtemaNf4XiBBpUW4YngS6 | + +--------------------------------------------------------------------+ | | 0xa2b6b947cf2b9bf6df03c8caab08e38ab951d8b120b9c37265d9be01d86bb170 | +------------------------------+--------------------------------------------------------------------+ | ICM Messenger Address | 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf | +------------------------------+--------------------------------------------------------------------+ | ICM Registry Address | 0x5DB9A7629912EBF95876228C24A848de0bfB43A9 | +------------------------------+--------------------------------------------------------------------+ ``` Controlling Relayer Execution[​](#controlling-relayer-execution "Direct link to heading") ----------------------------------------------------------------------------------------- CLI provides two commands to remotely control Relayer execution: ```bash avalanche interchain relayer stop --cluster ✓ Remote AWM Relayer on i-0f1815c016b555fcc successfully stopped ``` ```bash avalanche interchain relayer start --cluster ✓ Remote AWM Relayer on i-0f1815c016b555fcc successfully started ``` # Teleporter on Local Network (/docs/tooling/avalanche-cli/cross-chain/teleporter-local-network) --- title: Teleporter on Local Network description: This how-to guide focuses on deploying Teleporter-enabled Avalanche L1s to a local Avalanche network. --- After this tutorial, you would have created and deployed two Avalanche L1s to the local network and have enabled them to cross-communicate with each other and with the local C-Chain (through Teleporter and the underlying Warp technology.) For more information on cross chain messaging through Teleporter and Warp, check: - [Cross Chain References](/docs/cross-chain) Note that currently only [Subnet-EVM](https://github.com/ava-labs/subnet-evm) and [Subnet-EVM-Based](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1) virtual machines support Teleporter. ## Prerequisites - [Avalanche-CLI installed](/docs/tooling/avalanche-cli) ## Create Avalanche L1 Configurations Let's create an Avalanche L1 called `` with the latest Subnet-EVM version, a chain ID of 1, TOKEN1 as the token name, and with default Subnet-EVM parameters (more information regarding Avalanche L1 creation can be found [here](/docs/tooling/avalanche-cli#create-your-avalanche-l1-configuration)): ```bash avalanche blockchain create --evm --latest\ --evm-chain-id 1 --evm-token TOKEN1 --evm-defaults creating genesis for configuring airdrop to stored key "subnet__airdrop" with address 0x0EF8151A3e6ad1d4e17C8ED4128b20EB5edc58B1 loading stored key "cli-teleporter-deployer" for teleporter deploys (evm address, genesis balance) = (0xE932784f56774879e03F3624fbeC6261154ec711, 600000000000000000000) using latest teleporter version (v1.0.0) ✓ Successfully created subnet configuration ``` Notice that by default, Teleporter is enabled and a stored key is created to fund Teleporter related operations (that is deploy Teleporter smart contracts, fund Teleporter Relayer). To disable Teleporter in your Avalanche L1, use the flag `--teleporter=false` when creating the Avalanche L1. To disable Relayer in your Avalanche L1, use the flag `--relayer=false` when creating the Avalanche L1. Now let's create a second Avalanche L1 called ``, with similar settings: ```bash avalanche blockchain create --evm --latest\ --evm-chain-id 2 --evm-token TOKEN2 --evm-defaults creating genesis for configuring airdrop to stored key "subnet__airdrop" with address 0x0EF815FFFF6ad1d4e17C8ED4128b20EB5edAABBB loading stored key "cli-teleporter-deployer" for teleporter deploys (evm address, genesis balance) = (0xE932784f56774879e03F3624fbeC6261154ec711, 600000000000000000000) using latest teleporter version (v1.0.0) ✓ Successfully created subnet configuration ``` Deploy the Avalanche L1s to Local Network[​](#deploy-the-avalanche-l1s-to-local-network "Direct link to heading") ----------------------------------------------------------------------------------------------------- Let's deploy ``: ```bash avalanche blockchain deploy --local Deploying [] to Local Network Backend controller started, pid: 149427, output at: ~/.avalanche-cli/runs/server_20240229_165923/avalanche-cli-backend.log Booting Network. Wait until healthy... Node logs directory: ~/.avalanche-cli/runs/network_20240229_165923/node/logs Network ready to use. Deploying Blockchain. Wait until network acknowledges... Teleporter Messenger successfully deployed to c-chain (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) Teleporter Registry successfully deployed to c-chain (0x17aB05351fC94a1a67Bf3f56DdbB941aE6c63E25) Teleporter Messenger successfully deployed to (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) Teleporter Registry successfully deployed to (0x9EDc4cB4E781413b1b82CC3A92a60131FC111F58) Using latest awm-relayer version (v1.1.0) Executing AWM-Relayer... Blockchain ready to use. Local network node endpoints: +-------+-----------+------------------------------------------------------------------------------------+--------------------------------------------+ | NODE | VM | URL | ALIAS URL | +-------+-----------+------------------------------------------------------------------------------------+--------------------------------------------+ | node1 | | http://127.0.0.1:9650/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9650/ext/bc//rpc | +-------+-----------+------------------------------------------------------------------------------------+--------------------------------------------+ | node2 | | http://127.0.0.1:9652/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9652/ext/bc//rpc | +-------+-----------+------------------------------------------------------------------------------------+--------------------------------------------+ | node3 | | http://127.0.0.1:9654/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9654/ext/bc//rpc | +-------+-----------+------------------------------------------------------------------------------------+--------------------------------------------+ | node4 | | http://127.0.0.1:9656/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9656/ext/bc//rpc | +-------+-----------+------------------------------------------------------------------------------------+--------------------------------------------+ | node5 | | http://127.0.0.1:9658/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9658/ext/bc//rpc | +-------+-----------+------------------------------------------------------------------------------------+--------------------------------------------+ Browser Extension connection details (any node URL from above works): RPC URL: http://127.0.0.1:9650/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc Funded address: 0x0EF8151A3e6ad1d4e17C8ED4128b20EB5edc58B1 with 1000000 (10^18) - private key: 16289399c9466912ffffffdc093c9b51124f0dc54ac7a766b2bc5ccf558d8eee Network name: Chain ID: 1 Currency Symbol: TOKEN1 ``` Notice some details here: - Two smart contracts are deployed to each Avalanche L1: Teleporter Messenger and Teleporter Registry - Both Teleporter smart contracts are also deployed to `C-Chain` in the Local Network - [AWM Teleporter Relayer](https://github.com/ava-labs/awm-relayer) is installed, configured and executed in background (A Relayer [listens](/docs/cross-chain/teleporter/overview#data-flow) for new messages being generated on a source Avalanche L1 and sends them to the destination Avalanche L1.) CLI configures the Relayer to enable every Avalanche L1 to send messages to all other Avalanche L1s. If you add more Avalanche L1s, the Relayer will be automatically reconfigured. When deploying Avalanche L1 ``, the two Teleporter contracts will not be deployed to C-Chain in Local Network as they have already been deployed when we deployed the first Avalanche L1. ```bash avalanche blockchain deploy --local Deploying [] to Local Network Deploying Blockchain. Wait until network acknowledges... Teleporter Messenger has already been deployed to c-chain Teleporter Messenger successfully deployed to (0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf) Teleporter Registry successfully deployed to (0x9EDc4cB4E781413b1b82CC3A92a60131FC111F58) Using latest awm-relayer version (v1.1.0) Executing AWM-Relayer... Blockchain ready to use. Local network node endpoints: +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | NODE | VM | URL | ALIAS URL | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node1 | | http://127.0.0.1:9650/ext/bc/2tVGwEQmeXtdnFURW1YSq5Yf4jbJPfTBfVcu68KWHdHe5e5gX5/rpc | http://127.0.0.1:9650/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node1 | | http://127.0.0.1:9650/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9650/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node2 | | http://127.0.0.1:9652/ext/bc/2tVGwEQmeXtdnFURW1YSq5Yf4jbJPfTBfVcu68KWHdHe5e5gX5/rpc | http://127.0.0.1:9652/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node2 | | http://127.0.0.1:9652/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9652/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node3 | | http://127.0.0.1:9654/ext/bc/2tVGwEQmeXtdnFURW1YSq5Yf4jbJPfTBfVcu68KWHdHe5e5gX5/rpc | http://127.0.0.1:9654/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node3 | | http://127.0.0.1:9654/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9654/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node4 | | http://127.0.0.1:9656/ext/bc/2tVGwEQmeXtdnFURW1YSq5Yf4jbJPfTBfVcu68KWHdHe5e5gX5/rpc | http://127.0.0.1:9656/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node4 | | http://127.0.0.1:9656/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9656/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node5 | | http://127.0.0.1:9658/ext/bc/MzN4AbtFzQ3eKqPhFaDpwCMJmagciWSCgghkZx6YeC6jRdvb6/rpc | http://127.0.0.1:9658/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ | node5 | | http://127.0.0.1:9658/ext/bc/2tVGwEQmeXtdnFURW1YSq5Yf4jbJPfTBfVcu68KWHdHe5e5gX5/rpc | http://127.0.0.1:9658/ext/bc//rpc | +-------+-----------+-------------------------------------------------------------------------------------+--------------------------------------------+ Browser Extension connection details (any node URL from above works): RPC URL: http://127.0.0.1:9650/ext/bc/2tVGwEQmeXtdnFURW1YSq5Yf4jbJPfTBfVcu68KWHdHe5e5gX5/rpc Funded address: 0x0EF815FFFF6ad1d4e17C8ED4128b20EB5edAABBB with 1000000 (10^18) - private key: 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 Network name: Chain ID: 2 Currency Symbol: TOKEN2 ``` Verify Teleporter Is Successfully Set Up[​](#verify-teleporter-is-successfully-set-up "Direct link to heading") --------------------------------------------------------------------------------------------------------------- To verify that Teleporter is successfully, let's send a couple of cross messages (from C-Chain to chain1): ```bash avalanche teleporter sendMsg C-Chain chain1 "Hello World" --local ``` Results: ```bash Delivering message "this is a message" to source subnet "C-Chain" Waiting for message to be received at destination subnet subnet "chain1" Message successfully Teleported! ``` To verify that Teleporter is successfully deployed, let's send a couple of cross-chain messages (from chain2 to chain1): ```bash avalanche teleporter sendMsg chain2 chain1 "Hello World" --local ``` Results: ```bash Delivering message "this is a message" to source subnet "chain2" Waiting for message to be received at destination subnet subnet "chain1" Message successfully Teleported! ``` You have Teleport-ed your first message in the Local Network! Relayer related logs can be found at `~/.avalanche-cli/runs/awm-relayer.log`, and Relayer configuration can be found at `~/.avalanche-cli/runs/awm-relayer-config.json` Obtaining Information on Teleporter Deploys[​](#obtaining-information-on-teleporter-deploys "Direct link to heading") --------------------------------------------------------------------------------------------------------------------- ### Obtaining Avalanche L1 Information[​](#obtaining-avalanche-l1-information "Direct link to heading") By executing `blockchain describe` on a Teleporter enabled Avalanche L1, the following relevant information can be found: - Blockchain RPC URL - Blockchain ID in cb58 format - Blockchain ID in plain hex format - Teleporter Messenger address - Teleporter Registry address Let's get the information for ``: ```bash avalanche blockchain describe _____ _ _ _ | __ \ | | (_) | | | | | ___| |_ __ _ _| |___ | | | |/ _ \ __/ _ | | / __| | |__| | __/ || (_| | | \__ \ |_____/ \___|\__\__,_|_|_|___/ +--------------------------------+-------------------------------------------------------------------------------------+ | PARAMETER | VALUE | +--------------------------------+-------------------------------------------------------------------------------------+ | Blockchain Name | chain1 | +--------------------------------+-------------------------------------------------------------------------------------+ | ChainID | 1 | +--------------------------------+-------------------------------------------------------------------------------------+ | Token Name | TOKEN1 Token | +--------------------------------+-------------------------------------------------------------------------------------+ | Token Symbol | TOKEN1 | +--------------------------------+-------------------------------------------------------------------------------------+ | VM Version | v0.6.3 | +--------------------------------+-------------------------------------------------------------------------------------+ | VM ID | srEXiWaHjFEgKSgK2zBgnWQUVEy2MZA7UUqjqmBSS7MZYSCQ5 | +--------------------------------+-------------------------------------------------------------------------------------+ | Local Network SubnetID | 2CZP2ndbQnZxTzGuZjPrJAm5b4s2K2Bcjh8NqWoymi8NZMLYQk | +--------------------------------+-------------------------------------------------------------------------------------+ | Local Network RPC URL | http://127.0.0.1:9650/ext/bc/2cFWSgGkmRrmKtbPkB8yTpnq9ykK3Dc2qmxphwYtiGXCvnSwg8/rpc | +--------------------------------+-------------------------------------------------------------------------------------+ | Local Network BlockchainID | 2cFWSgGkmRrmKtbPkB8yTpnq9ykK3Dc2qmxphwYtiGXCvnSwg8 | + +-------------------------------------------------------------------------------------+ | | 0xd3bc5f71e6946d17c488d320cd1f6f5337d9dce75b3fac5023433c4634b6e91e | +--------------------------------+-------------------------------------------------------------------------------------+ | Local Network ICM | 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf | | Messenger Address | | +--------------------------------+-------------------------------------------------------------------------------------+ | Local Network ICM | 0xbD9e8eC38E43d34CAB4194881B9BF39d639D7Bd3 | | Registry Address | | +--------------------------------+-------------------------------------------------------------------------------------+ ... ``` ### Obtaining C-Chain Information[​](#obtaining-c-chain-information "Direct link to heading") Similar information can be found for C-Chain by using `primary describe`: ```bash avalanche primary describe --local _____ _____ _ _ _____ / ____| / ____| | (_) | __ \ | | ______| | | |__ __ _ _ _ __ | |__) |_ _ _ __ __ _ _ __ ___ ___ | | |______| | | '_ \ / _ | | '_ \ | ___/ _ | '__/ _ | '_ _ \/ __| | |____ | |____| | | | (_| | | | | | | | | (_| | | | (_| | | | | | \__ \ \_____| \_____|_| |_|\__,_|_|_| |_| |_| \__,_|_| \__,_|_| |_| |_|___/ +------------------------------+--------------------------------------------------------------------+ | PARAMETER | VALUE | +------------------------------+--------------------------------------------------------------------+ | RPC URL | http://127.0.0.1:9650/ext/bc/C/rpc | +------------------------------+--------------------------------------------------------------------+ | EVM Chain ID | 43112 | +------------------------------+--------------------------------------------------------------------+ | TOKEN SYMBOL | AVAX | +------------------------------+--------------------------------------------------------------------+ | Address | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | +------------------------------+--------------------------------------------------------------------+ | Balance | 49999489.829989485 | +------------------------------+--------------------------------------------------------------------+ | Private Key | 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 | +------------------------------+--------------------------------------------------------------------+ | BlockchainID | 2JeJDKL9Bvn1vLuuPL1DpUccBCVUh7iRnkv3a5pV9kJW5HbuQz | + +--------------------------------------------------------------------+ | | 0xabc1bd35cb7313c8a2b62980172e6d7ef42aaa532c870499a148858b0b6a34fd | +------------------------------+--------------------------------------------------------------------+ | ICM Messenger Address | 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf | +------------------------------+--------------------------------------------------------------------+ | ICM Registry Address | 0x17aB05351fC94a1a67Bf3f56DdbB941aE6c63E25 | +------------------------------+--------------------------------------------------------------------+ ``` Controlling Relayer Execution[​](#controlling-relayer-execution "Direct link to heading") ----------------------------------------------------------------------------------------- Besides having the option to not use a Relayer at Avalanche L1 creation time, the Relayer can be stopped and restarted on used request. To stop the Relayer: ```bash avalanche interchain relayer stop --local ✓ Local AWM Relayer successfully stopped ``` To start it again: ```bash avalanche interchain relayer start --local using latest awm-relayer version (v1.1.0) Executing AWM-Relayer... ✓ Local AWM Relayer successfully started Logs can be found at ~/.avalanche-cli/runs/awm-relayer.log ``` # Teleporter Token Bridge (/docs/tooling/avalanche-cli/cross-chain/teleporter-token-bridge) --- title: Teleporter Token Bridge description: Deploy an example Teleporter Token Bridge on the local network. --- Teleporter Token Bridge enables users to transfer tokens between Avalanche L1s. The bridge is a set of smart contracts that are deployed across multiple Avalanche L1s, and leverages Teleporter for cross-chain communication. For more information on Teleporter Token Bridge, check: - [Teleporter Token Bridge README](https://github.com/ava-labs/teleporter-token-bridge) ## How to Deploy Teleporter Token Bridge on a Local Network This how-to guide focuses on deploying Teleporter Token Bridge on a local Avalanche network. After this tutorial, you would have learned how to transfer an ERC-20 token between two Teleporter-enabled Avalanche L1s and between C-Chain and a Teleporter-enabled Avalanche L1. ## Prerequisites For our example, you will first need to create and deploy a Teleporter-enabled Avalanche L1 in Local Network. We will name our Avalanche L1 `testblockchain`. - To create a Teleporter-enabled Avalanche L1 configuration, [visit here](/docs/tooling/avalanche-cli/cross-chain/teleporter-local-network#create-subnet-configurations) - To deploy a Teleporter-enabled Avalanche L1, [visit here](/docs/tooling/avalanche-cli/cross-chain/teleporter-local-network#deploy-the-subnets-to-local-network) ## Deploy ERC-20 Token in C-Chain First, let's create an ERC-20 Token and deploy it to C-Chain. For our example, it will be called TOK. Sample script to deploy the ERC-20 Token can be found [here](https://github.com/ava-labs/avalanche-cli/blob/main/cmd/contractcmd/deploy_erc20.go). To deploy the ERC-20 Token to C-Chain, we will call: ```bash avalanche contract deploy erc20 ``` When the command is run, our EWOQ address `0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC` would have received 100000 TOK tokens in C-Chain. Note that `0x5DB9A7629912EBF95876228C24A848de0bfB43A9` is our ERC-20 Token address, which we will use in our next command. ## Deploy Teleporter Token Bridge Next, we will now deploy Teleporter Token Bridge to our Local Network, where we will deploy the Home Contract to C-Chain and the Remote Contract to our Avalanche L1. ```bash avalanche teleporter bridge deploy ✔ Local Network ✔ C-Chain ✔ Deploy a new Home for the token ✔ An ERC-20 token Enter the address of the ERC-20 Token: 0x5DB9A7629912EBF95876228C24A848de0bfB43A9 ✔ Subnet testblockchain Downloading Bridge Contracts Compiling Bridge Home Deployed to http://127.0.0.1:9650/ext/bc/C/rpc Home Address: 0x4Ac1d98D9cEF99EC6546dEd4Bd550b0b287aaD6D Remote Deployed to http://127.0.0.1:9650/ext/bc/2TnSWd7odhkDWKYFDZHqU7CvtY8G6m46gWxUnhJRNYu4bznrrc/rpc Remote Address: 0x7DD1190e6F6CE8eE13C08F007FdAEE2f881B45D0 ``` Before we transfer our ERC-20 token from C-Chain to our Avalanche L1, we will first call `avalanche key list` command to check our initial balances in C-Chain and Avalanche L1. We will inquire the balances of our ERC-20 Token TOK both in C-Chain and our Avalanche L1, which has the address of `0x5DB9A7629912EBF95876228C24A848de0bfB43A9` in the C-Chain and address of `0x7DD1190e6F6CE8eE13C08F007FdAEE2f881B45D0` in our Avalanche L1 `testblockchain`. ```bash `avalanche key list --local --keys ewoq,blockchain_airdrop --subnets c,testblockchain --tokens 0x5DB9A7629912EBF95876228C24A848de0bfB43A9,0x7DD1190e6F6CE8eE13C08F007FdAEE2f881B45D0` +--------+--------------------+------------+--------------------------------------------+---------------+-----------------+---------------+ | KIND | NAME | SUBNET | ADDRESS | TOKEN | BALANCE | NETWORK | +--------+--------------------+---------+--------------------------------------------+---------------+-----------------+---------------+ | stored | ewoq | testblockchain | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | TOK (0x7DD1.) | 0 | Local Network | + + +---------+--------------------------------------------+---------------+-----------------+---------------+ | | | C-Chain | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | TOK (0x5DB9.) | 100000.000000000| Local Network | + +--------------------+---------+--------------------------------------------+---------------+-----------------+---------------+ | | blockchain | testblockchain | 0x5a4601D594Aa3848cA5EE0770b7883d3DBC666f6 | TOK (0x7DD1.) | 0 | Local Network | + + _airdrop +---------+--------------------------------------------+---------------+-----------------+---------------+ | | | C-Chain | 0x5a4601D594Aa3848cA5EE0770b7883d3DBC666f6 | TOK (0x5DB9.) | 0 | Local Network | +--------+--------------------+------------+--------------------------------------------+---------------+-----------------+---------------+ ``` ## Transfer the Token from C-Chain to Our Avalanche L1 Now we will transfer 100 TOK tokens from our `ewoq` address in C-Chain to blockchain_airdrop address in our Avalanche L1 `testblockchain`. Note that we will be using the Home contract address `0x4Ac1d98D9cEF99EC6546dEd4Bd550b0b287aaD6D` and Remote contract address `0x7DD1190e6F6CE8eE13C08F007FdAEE2f881B45D0`. ```bash avalanche key transfer ✔ Local Network ✔ C-Chain ✔ Subnet testblockchain Enter the address of the Bridge on c-chain: 0x4Ac1d98D9cEF99EC6546dEd4Bd550b0b287aaD6D Enter the address of the Bridge on testblockchain: 0x7DD1190e6F6CE8eE13C08F007FdAEE2f881B45D0 ✔ ewoq ✔ Key ✔ blockchain_airdrop Amount to send (TOKEN units): 100 ``` ## Verify That Transfer Is Successful We will call `avalanche key list` command again to verify that the transfer is successful. `blockchain_airdrop` should now have 100 TOK tokens in our Avalanche L1 `testblockchain` and our EWOQ account now has 99900 TOK tokens in C-Chain. ```bash avalanche key list --local --keys ewoq,blockchain_airdrop --subnets c,testblockchain --tokens 0x5DB9A7629912EBF95876228C24A848de0bfB43A9,0x7DD1190e6F6CE8eE13C08F007FdAEE2f881B45D0 +--------+--------------------+------------+--------------------------------------------+---------------+-----------------+---------------+ | KIND | NAME | SUBNET | ADDRESS | TOKEN | BALANCE | NETWORK | +--------+--------------------+---------+--------------------------------------------+---------------+-----------------+---------------+ | stored | ewoq | testblockchain | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | TOK (0x7DD1.) | 0 | Local Network | + + +---------+--------------------------------------------+---------------+-----------------+---------------+ | | | C-Chain | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | TOK (0x5DB9.) | 99900.000000000 | Local Network | + +--------------------+---------+--------------------------------------------+---------------+-----------------+---------------+ | | blockchain | testblockchain | 0x5a4601D594Aa3848cA5EE0770b7883d3DBC666f6 | TOK (0x7DD1.) | 100.000000000 | Local Network | + + _airdrop +---------+--------------------------------------------+---------------+-----------------+---------------+ | | | C-Chain | 0x5a4601D594Aa3848cA5EE0770b7883d3DBC666f6 | TOK (0x5DB9.) | 0 | Local Network | +--------+--------------------+------------+--------------------------------------------+---------------+-----------------+---------------+ ``` And that's it! You have now successfully completed your first transfer from C-Chain to Avalanche L1 using Teleporter Token Bridge! # Import an Avalanche L1 (/docs/tooling/avalanche-cli/guides/import-avalanche-l1) --- title: Import an Avalanche L1 description: Learn how to import an Avalanche-l1 into Avalanche-CLI. --- Context[​](#context "Direct link to heading") --------------------------------------------- In previous instances, Avalanche L1s might have been manually created through transaction issuance to node APIs, whether it was done using a local node or public API nodes. However, the current focus is on integrating Avalanche-CLI. To achieve this integration, this guide demonstrates the process of importing an Avalanche L1 to the Avalanche-CLI to enable better management of the Avalanche L1's configuration. This how-to uses the BEAM Avalanche L1 deployed on Fuji Testnet as the example Avalanche L1. Requirements[​](#requirements "Direct link to heading") ------------------------------------------------------- For the import to work properly, you need: - The Avalanche L1's genesis file, stored on disk - The Avalanche L1's SubnetID Import the Avalanche L1[​](#import-the-avalanche-l1 "Direct link to heading") ----------------------------------------------------------------- For these use cases, Avalanche-CLI now supports the `import public` command. Start the import by issuing ``` avalanche blockchain import public ``` The tool prompts for the network from which to import. The invariant assumption here is that the network is a public network, either the Fuji testnet or Mainnet. In other words, importing from a local network isn't supported. ``` Use the arrow keys to navigate: ↓ ↑ → ← ? Choose a network to import from: ▸ Fuji Mainnet ``` As stated earlier, this is from Fuji, so select it. As a next step, Avalanche-CLI asks for the path of the genesis file on disk: ``` ✗ Provide the path to the genesis file: /tmp/subnet_evm.genesis.json ``` The wizard checks if the file at the provided path exists, refer to the checkmark at the beginning of the line: ``` ✔ Provide the path to the genesis file: /tmp/subnetevm_genesis.json ``` Subsequently, the wizard asks if nodes have already been deployed for this Avalanche L1. ``` Use the arrow keys to navigate: ↓ ↑ → ← ? Have nodes already been deployed to this subnet?: Yes ▸ No ``` ### Nodes are Already Validating This Avalanche L1[​](#nodes-are-already-validating-this-avalanche-l1 "Direct link to heading") If nodes already have been deployed, the wizard attempts to query such a node for detailed data like the VM version. This allows the tool to skip querying GitHub (or wherever the VM's repository is hosted) for the VM's version, but rather we'll get the exact version which is actually running on the node. For this to work, a node API URL is requested from the user, which is used for the query. This requires that the node's API IP and port are accessible from the machine running Avalanche-CLI, or the node is obviously not reachable, and thus the query times out and fails, and the tool exits. The node should also be validating the given Avalanche L1 for the import to be meaningful, otherwise, the import fails with missing information. If the query succeeded, the wizard jumps to prompt for the Avalanche L1 ID (SubnetID). ``` Please provide an API URL of such a node so we can query its VM version (e.g. http://111.22.33.44:5555): http://154.42.240.119:9650 What is the ID of the subnet?: ie1wUBR2bQDPkGCRf2CBVzmP55eSiyJsFYqeGXnTYt2r33aKW ``` The rest of the wizard is identical to the next section, except that there is no prompt for the VM version anymore. ### Nodes Aren't Yet Validating this Avalanche L1, the Nodes API URL are Unknown, or Inaccessible (Firewalls)[​](#nodes-arent-yet-validating-this-avalanche-l1-the-nodes-api-url-are-unknown-or-inaccessible-firewalls "Direct link to heading") If you don't have a node's API URL at hand, or it's not reachable from the machine running Avalanche-CLI, or maybe no nodes have even been deployed yet because only the `CreateSubnet` transaction has been issued, for example, you can query the public APIs. You can't know for sure what Avalanche L1 VM versions the validators are running though, therefore the tool has to prompt later. So, select `No` when the tool asks for deployed nodes: Thus, at this point the wizard requests the Avalanche L1's ID, without which it can't know what to import. Remember the ID is different on different networks. From the [Testnet Avalanche L1 Explorer](https://subnets-test.avax.network/beam) you can see that BEAM's Avalanche L1 ID (SubnetID) is `ie1wUBR2bQDPkGCRf2CBVzmP55eSiyJsFYqeGXnTYt2r33aKW`: ``` ✔ What is the ID of the subnet?: ie1wUBR2bQDPkGCRf2CBVzmP55eSiyJsFYqeGXnTYt2r33aKW ``` Notice the checkmark at line start, it signals that there is ID format validation. If you hit `enter` now, the tool queries the public APIs for the given network, and if successful, it prints some information about the Avalanche L1, and proceeds to ask about the Avalanche L1's type: ``` Getting information from the Fuji network... Retrieved information. BlockchainID: y97omoP2cSyEVfdSztQHXD9EnfnVP9YKjZwAxhUfGbLAPYT9t, Name: BEAM, VMID: kLPs8zGsTVZ28DhP1VefPCFbCgS7o5bDNez8JUxPVw9E6Ubbz Use the arrow keys to navigate: ↓ ↑ → ← ? What's this VM's type?: ▸ Subnet-EVM Custom ``` Avalanche-CLI needs to know the VM type, to hit its repository and select what VM versions are available. This works automatically for Ava Labs VMs (like Subnet-EVM). Custom VMs aren't supported yet at this point, but are next on the agenda. As the import is for BEAM, and you know that it's a Subnet-EVM type, select that. The tool then queries the (GitHub) repository for available releases, and prompts the user to pick the version she wants to use: ``` ✔ Subnet-EVM Use the arrow keys to navigate: ↓ ↑ → ← ? Pick the version for this VM: ▸ v0.4.5 v0.4.5-rc.1 v0.4.4 v0.4.4-rc.0 ↓ v0.4.3 ``` There is only so much the tool can help here, the Avalanche L1 manager/administrator should know what they want to use Avalanche-CLI for, how, and why they're importing the Avalanche L1. It's crucial to understand that the correct versions are only known to the user. The latest might be usually fine, but the tool can't make assumptions about it easily. This is why it's indispensable that the wizard prompts the user, and the tool requires her to choose. If you selected to query an actual Avalanche L1 validator, not the public APIs, in the preceding step. In such a scenario, the tool skips this picking. ``` ✔ v0.4.5 Subnet BEAM imported successfully ``` The choice finalizes the wizard, which hopefully signals that the import succeeded. If something went wrong, the error messages provide cause information. This means you can now use Avalanche-CLI to handle the imported Avalanche L1 in the accustomed way. For example, you could deploy the BEAM Avalanche L1 locally. For a complete description of options, flags, and the command, visit the [command reference](/docs/tooling/cli-commands#avalanche-l1-import). # Run with Docker (/docs/tooling/avalanche-cli/guides/run-with-docker) --- title: Run with Docker description: Instructions for running Avalanche-CLI in a Docker container. --- To run Avalanche-CLI in a docker container, you need to enable ipv6. Edit `/etc/docker/daemon.json`. Add this snippet then restart the docker service. ```json { "ipv6": true, "fixed-cidr-v6": "fd00::/80" } ``` # Add Validator (/docs/tooling/avalanche-cli/maintain/add-validator-l1) --- title: Add Validator description: Learn how to add a validator to an Avalanche L1. --- ### Add a Validator to an Avalanche L1 ```bash avalanche blockchain addValidator ``` #### Choose Network Choose the network where the operation will be ```bash ? Choose a network for the operation: ▸ Local Network Devnet Fuji Testnet Mainnet ``` #### Choose P-Chain Fee Payer Choose the key that will be used to pay for the transaction fees on the P-Chain. ```bash ? Which key should be used to pay for transaction fees on P-Chain?: ▸ Use stored key Use ledger ``` #### Enter Node ID Enter the NodeID of the node you want to add as a blockchain validator. ```bash ✗ What is the NodeID of the node you want to add as a blockchain validator?: ``` You can find the NodeID in the node's configuration file or the console when first started with avalanchego. An example of a NodeID is `NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg` #### Enter the BLS Public Key Enter the public key of the node's BLS ```bash Next, we need the public key and proof of possession of the node's BLS Check https://build.avax.network/docs/rpcs/other/info-rpc#infogetnodeid for instructions on calling info.getNodeID API ✗ What is the node's BLS public key?: ``` You can find the BLS public key in the node's configuration file or the console when first started with avalanchego. #### Enter BLS Proof of Possession Enter the proof of possession of the node's BLS ```bash ✗ What is the node's BLS proof of possession?: ``` You can find the BLS proof of possession in the node's configuration file or the console when first started with avalanchego. #### Enter AVAX Balance This balance will be used to pay the P-Chain continuous staking fee. ```bash ✗ What balance would you like to assign to the validator (in AVAX)?: ``` #### Enter Leftover AVAX Address This address will receive any leftover AVAX when the node is removed from the validator set. ```bash ? Which key would you like to set as change owner for leftover AVAX if the node is removed from validator set?: ▸ Get address from an existing stored key (created from avalanche key create or avalanche key import) Custom ``` #### Enter Disable Validator Address This address will be able to disable the validator using P-Chain transactions. ```bash Which address do you want to be able to disable the validator using P-Chain transactions?: ▸ Get address from an existing stored key (created from avalanche key create or avalanche key import) Custom ``` ### Proof of Stake specific parameters If your network was created with Proof of Stake validator manager, you will be asked for the following additional parameters. You can also pass these parameters as flags to the command. ```bash --delegation-fee uint16 delegation fee (in bips) --stake-amount uint amount of native tokens to stake --staking-period duration how long this validator will be staking ``` # Delete an Avalanche L1 (/docs/tooling/avalanche-cli/maintain/delete-avalanche-l1) --- title: Delete an Avalanche L1 description: Learn how to delete an Avalanche L1. --- Deleting an Avalanche L1 Configuration[​](#deleting-a-avalanche-l1-configuration "Direct link to heading") --------------------------------------------------------------------------------------------- To delete a created Avalanche L1 configuration, run: ```bash avalanche blockchain delete ``` Deleting a Deployed Avalanche L1[​](#deleting-a-deployed-avalanche-l1 "Direct link to heading") ----------------------------------------------------------------------------------- You can't delete Avalanche L1s deployed to Mainnet or the Fuji Testnet. However, you may delete Avalanche L1s deployed to a local network by cleaning the network state with below command: ```bash avalanche network clean ``` # Remove Validator (/docs/tooling/avalanche-cli/maintain/remove-validator-l1) --- title: Remove Validator description: Learn how to remove a validator from an Avalanche L1. --- ### Remove a Validator from an Avalanche L1 ```bash avalanche blockchain removeValidator ``` #### Choose the Network Choose the network where the validator is registered. ```bash ? Choose a network for the operation: ▸ Local Network Devnet Fuji Testnet Mainnet ``` #### Choose P-Chain fee payer Choose the key to pay for the transaction fees on the P-Chain. ```bash ? Which key should be used to pay for transaction fees on P-Chain?: ▸ Use stored key Use ledger ``` #### Enter Node-ID of the Validator to Remove Enter the Node-ID of the validator you want to remove. ```bash ✗ What is the NodeID of the node you want to remove as a blockchain validator?: ``` You can find the NodeID in the node's configuration file or the console when first started with avalanchego. An example of a NodeID is `NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg` #### Confirm the removal ```bash Validator manager owner 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC pays for the initialization of the validator's removal (Blockchain gas token) RPC Endpoint: http://127.0.0.1:9652/ext/bc/2qmU6w47Mp7D7fGhbRuZm6Z1Nn6FZXZAxKpaeTMFiRQW9CBErh/rpc Forcing removal of NodeID-7cQrriPWGXa5yuJGZUgsxxwH9j4T8pPkY as it is a PoS bootstrap validator Using validationID: 228zzCgDmAmuaJDGnFkFgnVqbbJPRF7qF1Xpd3dhtqGDhMjJK2 for nodeID: NodeID-7cQrriPWGXa5yuJGZUgsxxwH9j4T8pPkY ValidationID: 228zzCgDmAmuaJDGnFkFgnVqbbJPRF7qF1Xpd3dhtqGDhMjJK2 SetSubnetValidatorWeightTX fee: 0.000078836 AVAX SetSubnetValidatorWeightTx ID: 2FUimPZ37DscPJiQDLrtKtumER3LNr48MJi6VR2jGXkYEKpCaq ✓ Validator successfully removed from the Subnet ``` ### Proof of Authority Networks If the network is a Proof of Authority network, the validator must be removed from the network by the Validator Manager owner. ### Proof of Stake Networks If the network is a Proof of Stake network, the validator must be removed by the initial staker. Rewards will be distributed to the validator after the removal. It is important to note that the initial PoS Validator set are treated as bootstrap validators and are not elgible for rewards. # Troubleshooting (/docs/tooling/avalanche-cli/maintain/troubleshooting) --- title: Troubleshooting description: If you run into trouble deploying your Avalanche L1, use this document for tips to resolve common issues. --- Deployment Times Out[​](#deployment-times-out "Direct link to heading") ----------------------------------------------------------------------- During a local deployment, your network may fail to start. Your error may look something like this: ```bash [~]$ avalanche blockchain deploy myblockchain ✔ Local Network Deploying [myblockchain] to Local Network Backend controller started, pid: 26388, output at: /Users/user/.avalanche-cli/runs/server_20221231_111605/avalanche-cli-backend VMs ready. Starting network... .................................................................................. .................................................................................. ......Error: failed to query network health: rpc error: code = DeadlineExceeded desc = context deadline exceeded ``` Avalanche-CLI only supports running one local Avalanche network at a time. If other instances of AvalancheGo are running concurrently, your Avalanche-CLI network fails to start. To test for this error, start by shutting down any Avalanche nodes started by Avalanche-CLI. ```bash avalanche network clean --hard ``` Next, look for any lingering AvalancheGo processes with: ```bash ps aux | grep avalanchego ``` If any processes are running, you need to stop them before you can launch your VM with Avalanche-CLI. If you're running a validator node on the same box you're using Avalanche-CLI, **don't** end any of these lingering AvalancheGo processes. This may shut down your validator and could affect your validation uptime. Incompatible RPC Version for Custom VM[​](#incompatible-rpc-version-for-custom-vm "Direct link to heading") ----------------------------------------------------------------------------------------------------------- If you're locally deploying a custom VM, you may run into this error message. ```bash [~]$ avalanche blockchain deploy myblockchain ✔ Local Network Deploying [myblockchain] to Local Network Backend controller started, pid: 26388, output at: /Users/user/.avalanche-cli/runs/server_20221231_111605/avalanche-cli-backend VMs ready. Starting network... ......... Blockchain has been deployed. Wait until network acknowledges... .................................................................................. .................................................................................. ......Error: failed to query network health: rpc error: code = DeadlineExceeded desc = context deadline exceeded ``` This error has many possible causes, but a common cause is usually due to **an RPC protocol version mismatch.** AvalancheGo communicates with custom VMs over RPC using [gRPC](https://grpc.io/). gRPC defines a protocol specification shared by both AvalancheGo and the VM. **Both components must be running the same RPC version for VM deployment to work.** Your custom VM's RPC version is set by the version of AvalancheGo that you import. By default, Avalanche-CLI creates local Avalalanche networks that run the latest AvalancheGo release. ### Example[​](#example "Direct link to heading") Here's an example with real numbers from the AvalancheGo compatibility page: - If the latest AvalancheGo release is version v1.10.11, then Avalanche-CLI deploys a network with RPC version 28. - For your deploy to be successful, your VM must also have RPC version 28. Because only AvalancheGo versions v1.10.9, v1.10.10 and v1.10.11 supports RPC version 28, your VM **must** import one of those versions. ### Solution[​](#solution "Direct link to heading") Error: `RPCChainVM protocol version mismatch between AvalancheGo and Virtual Machine plugin` This error occurs when the RPCChainVM protocol version used by VMs like Subnet-EVM are incompatible with the protocol version of AvalancheGo. If your VM has an RPC version mismatch, you have two options: 1. Update the version of AvalancheGo you use in your VM. This is the correct long-term approach. 2. Use Avalanche-CLI to deploy an older version of AvalancheGo by using the `--avalanchego-version` flag. Both the [`blockchain deploy`](/docs/tooling/cli-commands#deploy) and [`network start`](/docs/tooling/cli-commands#start) commands support setting the AvalancheGo version explicitly. Although it's very important to keep your version of AvalancheGo up-to-date, this workaround helps you avoid broken builds in the short term. You must upgrade to the latest AvalancheGo version when deploying publicly to Fuji Testnet or Avalanche Mainnet. ### More Information[​](#more-information "Direct link to heading") Similar version matching is required in different tools on the ecosystem. Here is a compatibility table showing which RPCChainVM Version implements the more recent releases of AvalancheGo, Subnet-EVM, Precompile-EVM and HyperSDK. |RPCChainVM|AvalancheGo |Subnet-EVM |Precompile-EVM |HyperSDK | |----------|------------------|---------------|---------------|----------------| |26 |v1.10.1-v1.10.4 |v0.5.1-v0.5.2 |v0.1.0-v0.1.1 |v0.0.6-v0.0.9 | |27 |v1.10.5-v1.10.8 |v0.5.3 |v0.1.2 |v0.0.10-v0.0.12 | |28 |v1.10.9-v1.10.12 |v0.5.4-v0.5.6 |v0.1.3-v0.1.4 |v0.0.13-v0.0.15 | |30 |v1.10.15-v1.10.17 |v0.5.9-v0.5.10 |v0.1.6-v0.1.7 |- | |29 |v1.10.13-v1.10.14 |v0.5.7-v0.5.8 |v0.1.5 |- | |31 |v1.10.18- v1.10.19|v0.5.11 |v0.1.8 |v0.0.16 (latest)| |33 |v1.11.0 |v0.6.0-v0.6.1 |v0.2.0 |- | |34 |v1.11.1- v1.11.2 |v0.6.2 |- |- | |35 |v1.11.3 (latest) |v0.6.3 (latest)|v0.2.1 (latest)|- | You can view the full RPC compatibility broken down by release version for each tool here: [AvalancheGo](https://github.com/ava-labs/avalanchego/blob/master/version/compatibility.json). [Subnet-EVM](https://github.com/ava-labs/subnet-evm/blob/master/compatibility.json). [Precompile-EVM](https://github.com/ava-labs/precompile-evm/blob/main/compatibility.json). Updates to AvalancheGo's RPC version are **not** tied to its semantic version scheme. Minor AvalancheGo version bumps may include a breaking RPC version bump. Fix for MacBook Air M1/M2: ‘Bad CPU type in executable' Error[​](#fix-for-macbook-air-m1m2-bad-cpu-type-in-executable-error "Direct link to heading") ----------------------------------------------------------------------------------------------------------------------------------------------------- When running `avalanche blockchain deploy` via the Avalanche-CLI, the terminal may throw an error that contains the following: ```bash zsh: bad CPU type in executable: /Users/user.name/Downloads/build/avalanchego ``` This is because some Macs lack support for x86 binaries. Running the following command should fix this issue: ```bash /usr/sbin/softwareupdate --install-rosetta ``` # View Avalanche L1s (/docs/tooling/avalanche-cli/maintain/view-avalanche-l1s) --- title: View Avalanche L1s description: CLI commands for viewing avalanche-l1s. --- ## List Avalanche L1 Configurations You can list the Avalanche L1s you've created with: `avalanche blockchain list` Example: ```bash > avalanche blockchain list +--------------+--------------+----------+---------------------------------------------------+------------+------------+-----------+ | SUBNET | CHAIN | CHAINID | VMID | TYPE | VM VERSION | FROM REPO | +--------------+--------------+----------+---------------------------------------------------+------------+------------+-----------+ | myblockchain | myblockchain | 111 | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | Subnet-EVM | v0.7.0 | false | +--------------+--------------+----------+---------------------------------------------------+------------+------------+-----------+ ``` To see detailed information about your deployed Avalanche L1s, add the `--deployed` flag: ```bash > avalanche blockchain list --deployed +--------------+--------------+---------------------------------------------------+---------------+----------------+---------+ | SUBNET | CHAIN | VM ID | LOCAL NETWORK | FUJI (TESTNET) | MAINNET | +--------------+--------------+---------------------------------------------------+---------------+----------------+---------+ | myblockchain | myblockchain | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | Yes | No | No | +--------------+--------------+---------------------------------------------------+---------------+----------------+---------+ ```bash ## Describe Avalanche L1 Configurations To see the details of a specific configuration, run: `avalanche blockchain describe ` Example: ```bash > avalanche blockchain describe myblockchain +---------------------------------------------------------------------------------------------------------------------------------+ | MYBLOCKCHAIN | +---------------+-----------------------------------------------------------------------------------------------------------------+ | Name | myblockchain | +---------------+-----------------------------------------------------------------------------------------------------------------+ | VM ID | qDNV9vtxZYYNqm7TN1mYBuaaknLdefDbFK8bFmMLTJQJKaWjV | +---------------+-----------------------------------------------------------------------------------------------------------------+ | VM Version | v0.7.0 | +---------------+-----------------------------------------------------------------------------------------------------------------+ | Validation | Proof Of Authority | +---------------+--------------------------+--------------------------------------------------------------------------------------+ | Local Network | ChainID | 12345 | | +--------------------------+--------------------------------------------------------------------------------------+ | | SubnetID | fvx83jt2BWyibBRL4SRMa6WzjWp7GSFUeUUeoeBe1AqJ5Ey5w | | +--------------------------+--------------------------------------------------------------------------------------+ | | BlockchainID (CB58) | 2QGB9GbEhsFJLSRVii2mKs8dxugHzmK98G5391P2bvXSCb4sED | | +--------------------------+--------------------------------------------------------------------------------------+ | | BlockchainID (HEX) | 0xb883b54815c84a3f0903dbccd289ed5563395dd61c189db626e2d2680546b990 | | +--------------------------+--------------------------------------------------------------------------------------+ | | RPC Endpoint | http://127.0.0.1:60538/ext/bc/2QGB9GbEhsFJLSRVii2mKs8dxugHzmK98G5391P2bvXSCb4sED/rpc | +---------------+--------------------------+--------------------------------------------------------------------------------------+ +------------------------------------------------------------------------------------+ | ICM | +---------------+-----------------------+--------------------------------------------+ | Local Network | ICM Messenger Address | 0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf | | +-----------------------+--------------------------------------------+ | | ICM Registry Address | 0x695Ea5FbeBBdc99cA679F5fD7768f179d2281d74 | +---------------+-----------------------+--------------------------------------------+ +-------------------------------+ | TOKEN | +--------------+----------------+ | Token Name | TUTORIAL Token | +--------------+----------------+ | Token Symbol | TUTORIAL | +--------------+----------------+ +----------------------------------------------------------------------------------------------------------------------------------------+ | INITIAL TOKEN ALLOCATION | +-------------------------+------------------------------------------------------------------+---------------+---------------------------+ | DESCRIPTION | ADDRESS AND PRIVATE KEY | AMOUNT (OWEN) | AMOUNT (WEI) | +-------------------------+------------------------------------------------------------------+---------------+---------------------------+ | Main funded account | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | 1000000 | 1000000000000000000000000 | | ewoq | 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 | | | +-------------------------+------------------------------------------------------------------+---------------+---------------------------+ | Used by ICM | 0x001CBe3650FAD190d9ccBd57b289124F5131AA57 | 600 | 600000000000000000000 | | cli-teleporter-deployer | d00b93e1526d05a30b681911a3e0f5e5528add205880c1cafa4f84cdb2746b00 | | | +-------------------------+------------------------------------------------------------------+---------------+---------------------------+ +-----------------------------------------------------------------------------------------------------------------+ | SMART CONTRACTS | +-----------------------+--------------------------------------------+--------------------------------------------+ | DESCRIPTION | ADDRESS | DEPLOYER | +-----------------------+--------------------------------------------+--------------------------------------------+ | Proxy Admin | 0xC0fFEE1234567890aBCdeF1234567890abcDef34 | 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC | +-----------------------+--------------------------------------------+--------------------------------------------+ | PoA Validator Manager | 0x0C0DEbA5E0000000000000000000000000000000 | | +-----------------------+--------------------------------------------+--------------------------------------------+ | Transparent Proxy | 0x0Feedc0de0000000000000000000000000000000 | | +-----------------------+--------------------------------------------+--------------------------------------------+ +----------------------------------------------------------------------+ | INITIAL PRECOMPILE CONFIGS | +------------+-----------------+-------------------+-------------------+ | PRECOMPILE | ADMIN ADDRESSES | MANAGER ADDRESSES | ENABLED ADDRESSES | +------------+-----------------+-------------------+-------------------+ | Warp | n/a | n/a | n/a | +------------+-----------------+-------------------+-------------------+ +--------------------------------------------------------------------------+ | NODES | +-------+------------------------------------------+-----------------------+ | NAME | NODE ID | LOCALHOST ENDPOINT | +-------+------------------------------------------+-----------------------+ | node1 | NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg | http://127.0.0.1:9650 | +-------+------------------------------------------+-----------------------+ | node2 | NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ | http://127.0.0.1:9652 | +-------+------------------------------------------+-----------------------+ +--------------------------------------------------------------------------------------------------------+ | WALLET CONNECTION | +-----------------+--------------------------------------------------------------------------------------+ | Network RPC URL | http://127.0.0.1:60538/ext/bc/2QGB9GbEhsFJLSRVii2mKs8dxugHzmK98G5391P2bvXSCb4sED/rpc | +-----------------+--------------------------------------------------------------------------------------+ | Network Name | myblockchain | +-----------------+--------------------------------------------------------------------------------------+ | Chain ID | 12345 | +-----------------+--------------------------------------------------------------------------------------+ | Token Symbol | TUTORIAL | +-----------------+--------------------------------------------------------------------------------------+ | Token Name | TUTORIAL Token | +-----------------+--------------------------------------------------------------------------------------+ ``` ## Viewing a Genesis File If you'd like to see the raw genesis file, supply the `--genesis` flag to the describe command: `avalanche blockchain describe --genesis` Example: ```bash > avalanche blockchain describe myblockchain --genesis { "config": { "berlinBlock": 0, "byzantiumBlock": 0, "chainId": 111, "constantinopleBlock": 0, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0, "feeConfig": { "gasLimit": 12000000, "targetBlockRate": 2, "minBaseFee": 25000000000, "targetGas": 60000000, "baseFeeChangeDenominator": 36, "minBlockGasCost": 0, "maxBlockGasCost": 1000000, "blockGasCostStep": 200000 }, "homesteadBlock": 0, "istanbulBlock": 0, "londonBlock": 0, "muirGlacierBlock": 0, "petersburgBlock": 0, "warpConfig": { "blockTimestamp": 1734549536, "quorumNumerator": 67, "requirePrimaryNetworkSigners": true } }, "nonce": "0x0", "timestamp": "0x67632020", "extraData": "0x", "gasLimit": "0xb71b00", "difficulty": "0x0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "alloc": { "001cbe3650fad190d9ccbd57b289124f5131aa57": { "balance": "0x2086ac351052600000" }, "0c0deba5e0000000000000000000000000000000": { "code": "", "balance": "0x0", "nonce": "0x1" }, "0feedc0de0000000000000000000000000000000": { "code": "", "storage": { "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x0000000000000000000000000c0deba5e0000000000000000000000000000000", //sslot for proxy implementation "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000c0ffee1234567890abcdef1234567890abcdef34" //sslot for proxy admin }, "balance": "0x0", "nonce": "0x1" }, "8db97c7cece249c2b98bdc0226cc4c2a57bf52fc": { "balance": "0xd3c21bcecceda1000000" }, "c0ffee1234567890abcdef1234567890abcdef34": { "code": "", "storage": { "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fc" //sslot for owner }, "balance": "0x0", "nonce": "0x1" } }, "airdropHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "airdropAmount": null, "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "baseFeePerGas": null, "excessBlobGas": null, "blobGasUsed": null } ``` # Ledger P-Chain Transfer (/docs/tooling/avalanche-cli/transactions/ledger-p-chain-transfer) --- title: Ledger P-Chain Transfer description: Transferring funds between P-Chain using Avalanche CLI. --- Transferring funds between P-Chain wallets becomes necessary in certain situations: 1. Funds need to be sent to the Avalanche L1 control key, which might have a zero balance due to fee payments. The Avalanche L1 control key requires funding to ensure proper support for Avalanche L1 operations. 2. Funds need to be moved from one Ledger address index to another. A Ledger manages an infinite sequence of addresses all derived from a master private key and can sign for any of those addresses. Each one is referred to by an index, or the associated address. Avalanche-CLI usually expects to use index 0, but sometimes, the funds are in a different index. Occasionally, a transfer made to a ledger can be made to an address different from the default one used by the CLI. To enable direct transfers between P-Chain addresses, use the command `avalanche key transfer`. This operation involves a series of import/export actions with the P-Chain and X-Chain. The fee for this operation is four times the typical import operation fee, which comes out to 0.004 AVAX. You can find more information about fees [here](/docs/rpcs/other/guides/txn-fees). The `key transfer` command can also be applied to the stored keys managed by the CLI. It enables moving funds from one stored key to another, and from a ledger to a stored key or the other way. This how-to guide focuses on transferring funds between ledger accounts. ## Prerequisites - [`Avalanche-CLI`](/docs/tooling/avalanche-cli) installed - Multiple Ledger devices [configured for Avalanche](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-on-mainnet#setting-up-your-ledger) Example: Sending All Funds From One Ledger to Another[​](#example-sending-all-funds-from-one-ledger-to-another "Direct link to heading") ---------------------------------------------------------------------------------------------------------------------------------------- - Source address: ledger A, index 2 (the web wallet shows 4.5 AVAX for this ledger) - Target address: ledger B, index 0 (the web wallet shows 0 AVAX for this ledger) ### Determine Sender Address Index[​](#determine-sender-address-index "Direct link to heading") A ledger can manage an infinite amount of addresses derived from a main private key. Because of this, many operations require the user to specify an address index. After confirming with a web wallet that 4.5 AVAX is available on p-chain address `P-avax10an3cucdfqru984pnvv6y0rspvvclz63e523m0`, connect ledger A. With the avalanche app running, execute: ```bash avalanche key list --mainnet --ledger 0,1,2,3,4,5 ``` To see p-chain addresses and balances for the first 6 indices in the ledger derived owner addresses. ```bash +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | KIND | NAME | CHAIN | ADDRESS | BALANCE | NETWORK | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | ledger | index 0 | P-Chain (Bech32 format) | P-avax1g8yucm7j0cnwwru4rp5lkzw6dpdxjmc2rfkqs9 | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 1 | | P-avax1drppshkst2ccygyq37m2z9e3ex2jhkd2txcm5r | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 2 | | P-avax10an3cucdfqru984pnvv6y0rspvvclz63e523m0 | 4.5 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 3 | | P-avax1yfpm7v5y5rej2nu7t2r0ffgrlpfq36je0rc5k6 | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 4 | | P-avax17nqvwcqsa8ddgeww8gzmfe932pz2syaj2vyd89 | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 5 | | P-avax1jzvnd05vsfksrtatm2e3rzu6eux9a287493yf8 | 0 | Mainnet | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ ``` The address `P-avax10an3cucdfqru984pnvv6y0rspvvclz63e523m0` has 4.5 AVAX and is associated with index 2 of ledger A. ### Determine Receiver Address Index[​](#determine-receiver-address-index "Direct link to heading") In this case the user wants to use index 0, the one CLI by default expects to contain funds. For the transfer command, it is also needed to know the target p-chain address. Do the following to obtain it: With the ledger B connected and the avalache app running, execute: ```bash avalanche key list --mainnet --ledger 0 ``` ```bash +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | KIND | NAME | CHAIN | ADDRESS | BALANCE | NETWORK | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | ledger | index 0 | P-Chain (Bech32 format) | P-avax1r4aceznjkz8ch4pmpqrmkq4f3sl952mdrdt6xm | 0 | Mainnet | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ ``` Target address to be used is `P-avax1r4aceznjkz8ch4pmpqrmkq4f3sl952mdrdt6xm`, containing 0 funds. ### Send the Transfer[​](#send-the-transfer "Direct link to heading") A P-Chain to P-chain transfer is a two-part operation. There is no need for the two parts to be executed on the same machine, only for them to have some common params. For each part, the appropriate ledger (either source or target) must be connected to the machine executing it. The first step moves the money out of the source account into a X-Chain account owner by the receiver. It needs to be signed by the sending ledger. Enter the amount of AVAX to send to the recipient. This amount does not include fees. Note that the sending ledger pays all the fees. Then start the command: ```bash avalanche key transfer ``` First step is to specify the network. `Mainnet` in this case: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Network to use: ▸ Mainnet Fuji Local Network ``` Next, the step of the transfer must be specified. Send in this case: ```bash ? Step of the transfer: ▸ Send Receive ``` Next, the key source for the sender address. That is, the key that is going to sign the sending transactions. Select `Use ledger`: ```bash ? Which key source should be used to for the sender address?: Use stored key ▸ Use ledger ``` Next, the ledger index is asked for. Input `2`: ```bash ✗ Ledger index to use: 2 ``` Next, the amount to be sent is asked for: ```bash ✗ Amount to send (AVAX units): 4.496 ``` The, the target address is required: ```bash ✗ Receiver address: P-avax1r4aceznjkz8ch4pmpqrmkq4f3sl952mdrdt6xm ``` After that, a confirmation message is printed. Read carefully and choose `Yes`: ```bash this operation is going to: - send 4.496000000 AVAX from P-avax10an3cucdfqru984pnvv6y0rspvvclz63e523m0 to target address P-avax1r4aceznjkz8ch4pmpqrmkq4f3sl952mdrdt6xm - take a fee of 0.004000000 AVAX from source address P-avax10an3cucdfqru984pnvv6y0rspvvclz63e523m0 Use the arrow keys to navigate: ↓ ↑ → ← ? Confirm transfer: No ▸ Yes ``` After this, the first part is completed: ### Receive the Transfer[​](#receive-the-transfer "Direct link to heading") In this step, Ledger B signs the transaction to receive the funds. It imports the funds on the X-Chain before exporting them back to the desired P-Chain address. Connect ledger B and execute avalanche app. Then start the command: ```bash avalanche key transfer ``` Specify the `Mainnet` network: ```bash Use the arrow keys to navigate: ↓ ↑ → ← ? Network to use: ▸ Mainnet Fuji Local Network ``` Next, the step of the transfer must be specified. Receive in this case: ```bash ? Step of the transfer: Send ▸ Receive ``` Then, select Ledger as the key source that is going to sign the receiver operations. ```bash ? Which key source should be used to for the receiver address?: Use stored key ▸ Use ledger ``` Next, the ledger index is asked for. Input `0`: ```bash ✗ Ledger index to use: 0 ``` Next, the amount to receive is asked for: ```bash ✗ Amount to send (AVAX units): 4.496 ``` After that, a confirmation message is printed. Select `Yes`: ```bash this operation is going to: - receive 4.496000000 AVAX at target address P-avax1r4aceznjkz8ch4pmpqrmkq4f3sl952mdrdt6xm: Use the arrow keys to navigate: ↓ ↑ → ← ? Confirm transfer: No ▸ Yes ``` Finally, the second part of the operation is executed and the transfer is completed. ```bash Issuing ImportTx P -> X Issuing ExportTx X -> P Issuing ImportTx X -> P ``` ### Verifying Results of the Transfer Operation using `key list`[​](#verifying-results-of-the-transfer-operation-using-key-list "Direct link to heading") First verify ledger A accounts. Connect ledger A and open the avalanche app: ```bash avalanche key list --mainnet --ledger 0,1,2,3,4,5 ``` With result: ```bash +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | KIND | NAME | CHAIN | ADDRESS | BALANCE | NETWORK | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | ledger | index 0 | P-Chain (Bech32 format) | P-avax1g8yucm7j0cnwwru4rp5lkzw6dpdxjmc2rfkqs9 | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 1 | | P-avax1drppshkst2ccygyq37m2z9e3ex2jhkd2txcm5r | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 2 | | P-avax10an3cucdfqru984pnvv6y0rspvvclz63e523m0 | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 3 | | P-avax1yfpm7v5y5rej2nu7t2r0ffgrlpfq36je0rc5k6 | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 4 | | P-avax17nqvwcqsa8ddgeww8gzmfe932pz2syaj2vyd89 | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 5 | | P-avax1jzvnd05vsfksrtatm2e3rzu6eux9a287493yf8 | 0 | Mainnet | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ ``` Next, verify ledger B accounts. Connect ledger B and open the avalanche app: ```bash avalanche key list --mainnet --ledger 0,1,2,3,4,5 ``` With result: ```bash +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | KIND | NAME | CHAIN | ADDRESS | BALANCE | NETWORK | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ | ledger | index 0 | P-Chain (Bech32 format) | P-avax1r4aceznjkz8ch4pmpqrmkq4f3sl952mdrdt6xm | 4.496 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 1 | | P-avax18e9qsm30du590lhkwydhmkfwhcc9999gvxcaez | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 2 | | P-avax1unkkjstggvdty5gtnfhc0mgnl7qxa52z2d4c9y | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 3 | | P-avax1ek7n0zky3py7prxcrgnmh44y3wm6lc7r7x5r8e | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 4 | | P-avax1rsz6nt6qht5ep37qjk7ht0u9h30mgfhehsmqea | 0 | Mainnet | + +---------+ +-----------------------------------------------+---------+---------+ | | index 5 | | P-avax17u5wm4tfex7xr27xlwejm28pyk84tj0jzp42zz | 0 | Mainnet | +--------+---------+-------------------------+-----------------------------------------------+---------+---------+ ``` ### Recovery Steps[​](#recovery-steps "Direct link to heading") As a multi step operation, the receiving part of the transfer can have intermediate errors, due for example to temporal network connections on the client side. The CLI is going to capture errors and provide the user with a recovery message of the kind: ```bash ERROR: restart from this step by using the same command with extra arguments: --receive-recovery-step 1 ``` If this happen, the receiving operation should be started the same way, choosing the same options, but adding the extra suggested parameter: ```bash avalanche key transfer --receive-recovery-step 1 ``` Then, the CLI is going to resume where it left off. # Send AVAX on C/P-Chain (/docs/tooling/avalanche-cli/transactions/native-send) --- title: Send AVAX on C/P-Chain description: Learn how to execute a native transfer on the C or P-Chain using the Avalanche CLI. --- # Prerequisites - Install the [Avalanche CLI](/docs/tooling/avalanche-cli). - Use the CLI to [create a key](/docs/tooling/cli-commands#key-create). - Fund the key with AVAX. You can use the [faucet](https://test.core.app/tools/testnet-faucet/?subnet=c&token=c) with coupon code `devrel-avax-0112` to get testnet AVAX. - *Optionally*, you can [export](/docs/tooling/cli-commands#key-export) your private key for use in scripting or other tools. ## Initiate the `transfer` Command and Walk Through the Prompts In your terminal, run the following command: ```zsh avalanche key transfer ``` This command and all of its flags are documented [here](/docs/tooling/cli-commands#key-transfer). You will be prompted to answer the following questions: ```zsh ? On what Network do you want to execute the transfer?: ▸ Mainnet Fuji Testnet Devnet Local Network ``` If you select "Devnet", you must input the RPC URL. If your devnet's C-Chain RPC is `https://demo.avax-dev.network/ext/bc/C/rpc`, you should input the URL as: ```zsh ✔ Devnet Endpoint: https://demo.avax-dev.network ``` Select the chain you want to transfer funds from: ```zsh ? Where are the funds to transfer?: ▸ P-Chain C-Chain My blockchain isn't listed ``` Select the chain you want to transfer funds to: ```zsh ? Destination Chain: ▸ P-Chain X-Chain ``` Select the step of the transfer process you want to execute: ```zsh ? Step of the transfer: ▸ Send Receive ``` If you are performing a native transfer where the sender and receiver address are on the same chain, you only need to complete a "send" transaction. If you wish to perform a cross-chain transfer (i.e. from C to P-Chain), you should abort this flow and reinitiate the command as `avalanche key transfer --fund-p-chain` or `avalanche key transfer --fund-x-chain`, completing both the "send" and "receive" flows with keys stored in the CLI. You can fund your CLI-stored key with AVAX on the C-Chain using the [faucet](https://test.core.app/tools/testnet-faucet/?subnet=c&token=c) with coupon code `devrel-avax-0112`. Select the sender address: ```zsh ? Which key should be used as the sender?: ▸ Use stored key Use ledger ? Which stored key should be used as the sender address?: ▸ DemoKey MyKey ewoq ``` Specify the amount to send, input the destination address: ```zsh ✗ Amount to send (AVAX units): 100 ✗ Destination address: P-avax1zgjx8zj7z7zj7z7zj7z7zj7z7zj7zj7zj7zj7e ``` Review the transaction details and confirm/abort: ```zsh this operation is going to: - send 100.000000000 AVAX from P-avax1gmuqt8xg9j4h88kj3hyprt23nf50azlfg8txn2 to destination address P-avax1f630gvct4ht35ragcheapnn2n5cv2tkmq73ec0 - take a fee of 0.001000000 AVAX from source address P-avax1gmuqt8xg9j4h88kj3hyprt23nf50azlfg8txn2 ? Confirm transfer: No ▸ Yes ``` After a successful transfer, you can check your CLI keys' balances with the [command](/docs/tooling/cli-commands#key-list): `avalanche key list`. # Precompile Configs (/docs/tooling/avalanche-cli/upgrade/avalanche-l1-precompile-config) --- title: Precompile Configs description: Learn how to upgrade your Subnet-EVM precompile configurations. --- You can customize Subnet-EVM based Avalanche L1s after deployment by enabling and disabling precompiles. To do this, create a `upgrade.json` file and place it in the appropriate directory. This document describes how to perform such network upgrades. It's specific for Subnet-EVM upgrades. The document [Upgrade an Avalanche L1](/docs/avalanche-l1s/upgrade/considerations) describes all the background information required regarding Avalanche L1 upgrades. It's very important that you have read and understood the previously linked document. Failing to do so can potentially grind your network to a halt. This tutorial assumes that you have already [installed](/docs/tooling/avalanche-cli) Avalanche-CLI. It assumes you have already created and deployed an Avalanche L1 called `testblockchain`. Generate the Upgrade File[​](#generate-the-upgrade-file "Direct link to heading") --------------------------------------------------------------------------------- The [Precompiles](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#network-upgrades-enabledisable-precompiles) documentation describes what files the network upgrade requires, and where to place them. To generate a valid `upgrade.json` file, run: ```bash avalanche blockchain upgrade generate testblockchain ``` If you didn't create `testblockchain` yet, you would see this result: ```bash avalanche blockchain upgrade generate testblockchain The provided Avalanche L1 name "testblockchain" does not exist ``` Again, it makes no sense to try the upgrade command if the Avalanche L1 doesn't exist. If that's the case, please go ahead and [create](/docs/tooling/avalanche-cli) the Avalanche L1 first. If the Avalanche L1 definition exists, the tool launches a wizard. It may feel a bit redundant, but you first see some warnings, to draw focus to the dangers involved: ```bash avalanche blockchain upgrade generate testblockchain Performing a network upgrade requires coordinating the upgrade network-wide. A network upgrade changes the rule set used to process and verify blocks, such that any node that upgrades incorrectly or fails to upgrade by the time that upgrade goes into effect may become out of sync with the rest of the network. Any mistakes in configuring network upgrades or coordinating them on validators may cause the network to halt and recovering may be difficult. Please consult https://build.avax.network/docs/subnets/customize-a-subnet#network-upgrades-enabledisable-precompiles for more information Use the arrow keys to navigate: ↓ ↑ → ← ? Press [Enter] to continue, or abort by choosing 'no': ▸ Yes No ``` Go ahead and select `Yes` if you understand everything and you agree. You see a last note, before the actual configuration wizard starts: ```bash Avalanchego and this tool support configuring multiple precompiles. However, we suggest to only configure one per upgrade. Use the arrow keys to navigate: ↓ ↑ → ← ? Select the precompile to configure: ▸ Contract Deployment Allow List Manage Fee Settings Native Minting Transaction Allow List ``` Refer to [Precompiles](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#precompiles) for a description of available precompiles and how to configure them. Make sure you understand precompiles thoroughly and how to configure them before attempting to continue. For every precompile in the list, the wizard guides you to provide correct information by prompting relevant questions. For the sake of this tutorial, select `Transaction Allow List`. The document [Restricting Who Can Submit Transactions](/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#restricting-who-can-submit-transactions) describes what this precompile is about. ```bash ✔ Transaction Allow List Set parameters for the "Manage Fee Settings" precompile Use the arrow keys to navigate: ↓ ↑ → ← ? When should the precompile be activated?: ▸ In 5 minutes In 1 day In 1 week In 2 weeks Custom ``` This is actually common to all precompiles: they require an activation timestamp. If you think about it, it makes sense: you want a synchronized activation of your precompile. So think for a moment about when you want to set the activation timestamp to. You can select one of the suggested times in the future, or you can pick a custom one. After picking `Custom`, it shows the following prompt: ```bash ✔ Custom ✗ Enter the block activation UTC datetime in 'YYYY-MM-DD HH:MM:SS' format: ``` The format is `YYYY-MM-DD HH:MM:SS`, therefore `2023-03-31 14:00:00` would be a valid timestamp. Notice that the timestamp is in UTC. Please make sure you have converted the time from your timezone to UTC. Also notice the `✗` at the beginning of the line. The CLI tool does input validation, so if you provide a valid timestamp, the `x` disappears: ```bash ✔ Enter the block activation UTC datetime in 'YYYY-MM-DD HH:MM:SS' format: 2023-03-31 14:00:00 ``` The timestamp must be in the **future**, so make sure you use such a timestamp should you be running this tutorial after `2023-03-31 14:00:00`. After you provided the valid timestamp, proceed with the precompile specific configurations: ```bash The chosen block activation time is 2023-03-31 14:00:00 Use the arrow keys to navigate: ↓ ↑ → ← ? Add 'adminAddresses'?: ▸ Yes No ``` This will enable the addresses added in this section to add other admins and/or add enabled addresses for transaction issuance. The addresses provided in this tutorial are fake. However, make sure you or someone you trust have full control over the addresses. Otherwise, you might bring your Avalanche L1 to a halt. ```bash ✔ Yes Use the arrow keys to navigate: ↓ ↑ → ← ? Provide 'adminAddresses': ▸ Add Delete Preview More Info ↓ Done ``` The prompting runs with a pattern used throughout the tool: 1. Select an operation: - `Add`: adds a new address to the current list - `Delete`: removes an address from the current list - `Preview`: prints the current list 2. `More info` prints additional information for better guidance, if available 3. Select `Done` when you completed the list Go ahead and add your first address: ```bash ✔ Add ✔ Add an address: 0xaaaabbbbccccddddeeeeffff1111222233334444 ``` Add another one: ```bash ✔ Add Add an address: 0xaaaabbbbccccddddeeeeffff1111222233334444 ✔ Add ✔ Add an address: 0x1111222233334444aaaabbbbccccddddeeeeffff ``` Select `Preview` this time to confirm the list is correct: ```bash ✔ Preview 0. 0xaaaAbbBBCccCDDddEeEEFFfF1111222233334444 1. 0x1111222233334444aAaAbbBBCCCCDdDDeEeEffff Use the arrow keys to navigate: ↓ ↑ → ← ? Provide 'adminAddresses': ▸ Add Delete Preview More Info ↓ Done ``` If it looks good, select `Done` to continue: ```bash ✔ Done Use the arrow keys to navigate: ↓ ↑ → ← ? Add 'enabledAddresses'?: ▸ Yes No ``` Add one such enabled address, these are addresses which can issue transactions: ```bash ✔ Add ✔ Add an address: 0x55554444333322221111eeeeaaaabbbbccccdddd█ ``` After you added this address, and selected `Done`, the tool asks if you want to add another precompile: ```bash ✔ Done Use the arrow keys to navigate: ↓ ↑ → ← ? Should we configure another precompile?: ▸ No Yes ``` If you needed to add another one, you would select `Yes` here. The wizard would guide you through the other available precompiles, excluding already configured ones. To avoid making this tutorial too long, the assumption is you're done here. Select `No`, which ends the wizard. This means you have successfully terminated the generation of the upgrade file, often called upgrade bytes. The tool stores them internally. You shouldn't move files around manually. Use the `export` and `import` commands to get access to the files. So at this point you can either: - Deploy your upgrade bytes locally - Export your upgrade bytes to a file, for installation on a validator running on another machine - Import a file into a different machine running Avalanche-CLI How To Upgrade a Local Network[​](#how-to-upgrade-a-local-network "Direct link to heading") ------------------------------------------------------------------------------------------- The normal use case for this operation is that: - You already created an Avalanche L1 - You already deployed the Avalanche L1 locally - You already generated the upgrade file with the preceding command or imported into the tool - This tool already started the network If the preceding requirements aren't met, the network upgrade command fails. Therefore, to apply your generated or imported upgrade configuration: ```bash avalanche blockchain upgrade apply testblockchain ``` A number of checks run. For example, if you created the Avalanche L1 but didn't deploy it locally: ```bash avalanche blockchain upgrade apply testblockchain Error: no deployment target available Usage: avalanche blockchain upgrade apply [blockchainName] [flags] Flags: --avalanchego-chain-config-dir string avalanchego's chain config file directory (default "/home/fabio/.avalanchego/chains") --config create upgrade config for future Avalanche L1 deployments (same as generate) --fuji fuji apply upgrade existing fuji deployment (alias for `testnet`) -h, --help help for apply --local local apply upgrade existing local deployment --mainnet mainnet apply upgrade existing mainnet deployment --print if true, print the manual config without prompting (for public networks only) --testnet testnet apply upgrade existing testnet deployment (alias for `fuji`) Global Flags: --log-level string log level for the application (default "ERROR") ``` Go ahead and [deploy](/docs/tooling/avalanche-cli/create-deploy-avalanche-l1s/deploy-locally) first your Avalanche L1 if that's your case. If you already had deployed the Avalanche L1 instead, you see something like this: ```bash avalanche blockchain upgrade apply testblockchain Use the arrow keys to navigate: ↓ ↑ → ← ? What deployment would you like to upgrade: ▸ Existing local deployment ``` Select `Existing local deployment`. This installs the upgrade file on all nodes of your local network running in the background. Et voilà. This is the output shown if all went well: ```bash ✔ Existing local deployment ....... Network restarted and ready to use. Upgrade bytes have been applied to running nodes at these endpoints. The next upgrade will go into effect 2023-03-31 09:00:00 +-------+------------+-----------------------------------------------------------------------------------+ | NODE | VM | URL | +-------+------------+-----------------------------------------------------------------------------------+ | node1 | testblockchain | http://0.0.0.0:9650/ext/bc/2YTRV2roEhgvwJz7D7vr33hUZscpaZgcYgUTjeMK9KH99NFnsH/rpc | +-------+------------+-----------------------------------------------------------------------------------+ | node2 | testblockchain | http://0.0.0.0:9652/ext/bc/2YTRV2roEhgvwJz7D7vr33hUZscpaZgcYgUTjeMK9KH99NFnsH/rpc | +-------+------------+-----------------------------------------------------------------------------------+ | node3 | testblockchain | http://0.0.0.0:9654/ext/bc/2YTRV2roEhgvwJz7D7vr33hUZscpaZgcYgUTjeMK9KH99NFnsH/rpc | +-------+------------+-----------------------------------------------------------------------------------+ | node4 | testblockchain | http://0.0.0.0:9656/ext/bc/2YTRV2roEhgvwJz7D7vr33hUZscpaZgcYgUTjeMK9KH99NFnsH/rpc | +-------+------------+-----------------------------------------------------------------------------------+ | node5 | testblockchain | http://0.0.0.0:9658/ext/bc/2YTRV2roEhgvwJz7D7vr33hUZscpaZgcYgUTjeMK9KH99NFnsH/rpc | +-------+------------+-----------------------------------------------------------------------------------+ ``` There is only so much the tool can do here for you. It installed the upgrade bytes _as-is_ as you configured respectively provided them to the tool. You should verify yourself that the upgrades were actually installed correctly, for example issuing some transactions - mind the timestamp!. Apply the Upgrade to a Public Node (Fuji or Mainnet)[​](#apply-the-upgrade-to-a-public-node-fuji-or-mainnet "Direct link to heading") ------------------------------------------------------------------------------------------------------------------------------------- For this scenario to work, you should also have deployed the Avalanche L1 to the public network (Fuji or Mainnet) with this tool. Otherwise, the tool won't know the details of the Avalanche L1, and won't be able to guide you. Assuming the Avalanche L1 has been already deployed to Fuji, when running the `apply` command, the tool notices the deployment: ```bash avalanche blockchain upgrade apply testblockchain Use the arrow keys to navigate: ↓ ↑ → ← ? What deployment would you like to upgrade: Existing local deployment ▸ Fuji ``` If not, you would not find the `Fuji` entry here. This scenario assumes that you are running the `fuji` validator on the same machine which is running Avalanche-CLI. If this is the case, the tool tries to install the upgrade file at the expected destination. If you use default paths, it tries to install at `$HOME/.avalanchego/chains/`, creating the chain id directory, so that the file finally ends up at `$HOME/.avalanchego/chains//upgrade.json`. If you are _not_ using default paths, you can configure the path by providing the flag `--avalanchego-chain-config-dir` to the tool. For example: ```bash avalanche blockchain upgrade apply testblockchain --avalanchego-chain-config-dir /path/to/your/chains ``` Make sure to identify correctly where your chain config dir is, or the node might fail to find it. If all is correct, the file gets installed: ```bash avalanche blockchain upgrade apply testblockchain ✔ Fuji The chain config dir avalanchego uses is set at /home/fabio/.avalanchego/chains Trying to install the upgrade files at the provided /home/fabio/.avalanchego/chains path Successfully installed upgrade file ``` If however the node is _not_ running on this same machine where you are executing Avalanche-CLI, there is no point in running this command for a Fuji node. In this case, you might rather export the file and install it at the right location. To see the instructions about how to go about this, add the `--print` flag: ```bash avalanche blockchain upgrade apply testblockchain --print ✔ Fuji To install the upgrade file on your validator: 1. Identify where your validator has the avalanchego chain config dir configured. The default is at $HOME/.avalanchego/chains (/home/user/.avalanchego/chains on this machine). If you are using a different chain config dir for your node, use that one. 2. Create a directory with the blockchainID in the configured chain-config-dir (e.g. $HOME/.avalanchego/chains/ExDKhjXqiVg7s35p8YJ56CJpcw6nJgcGCCE7DbQ4oBknZ1qXi) if doesn't already exist. 3. Create an `upgrade.json` file in the blockchain directory with the content of your upgrade file. This is the content of your upgrade file as configured in this tool: { "precompileUpgrades": [ { "txAllowListConfig": { "adminAddresses": [ "0xb3d82b1367d362de99ab59a658165aff520cbd4d" ], "enabledAddresses": null, "blockTimestamp": 1677550447 } } ] } ****************************************************************************************************************** * Upgrades are tricky. The syntactic correctness of the upgrade file is important. * * The sequence of upgrades must be strictly observed. * * Make sure you understand https://build.avax.network/docs/nodes/configure/configs-flags#subnet-chain-configs * * before applying upgrades manually. * ****************************************************************************************************************** ``` The instructions also show the content of your current upgrade file, so you can just select that if you wish. Or actually export the file. Export the Upgrade File[​](#export-the-upgrade-file "Direct link to heading") ----------------------------------------------------------------------------- If you have generated the upgrade file, you can export it: ```bash avalanche blockchain upgrade export testblockchain ✔ Provide a path where we should export the file to: /tmp/testblockchain-upgrade.json ``` Just provide a valid path to the prompt, and the tool exports the file there. ```bash avalanche blockchain upgrade export testblockchain Provide a path where we should export the file to: /tmp/testblockchain-upgrade.json Writing the upgrade bytes file to "/tmp/testblockchain-upgrade.json"... File written successfully. ``` You can now take that file and copy it to validator nodes, see preceding instructions. Import the Upgrade File[​](#import-the-upgrade-file "Direct link to heading") ----------------------------------------------------------------------------- You or someone else might have generated the file elsewhere, or on another machine. And now you want to install it on the validator machine, using Avalanche-CLI. You can import the file: ```bash avalanche blockchain upgrade import testblockchain Provide the path to the upgrade file to import: /tmp/testblockchain-upgrade.json ``` An existing file with the same path and filename would be overwritten. After you have imported the file, you can `apply` it either to a local network or to a locally running validator. Follow the instructions for the appropriate use case. # Virtual Machine (/docs/tooling/avalanche-cli/upgrade/avalanche-l1-virtual-machine) --- title: Virtual Machine description: This how-to guide explains how to upgrade the VM of an already-deployed Avalanche L1. --- To upgrade a local Avalanche L1, you first need to pause the local network. To do so, run: ```bash avalanche network stop ``` Next, you need to select the new VM to run your Avalanche L1 on. If you're running a Subnet-EVM Avalanche L1, you likely want to bump to the latest released version. If you're running a Custom VM, you'll want to choose another custom binary. Start the upgrade wizard with: ```bash avalanche blockchain upgrade vm ``` where you replace `` with the name of the Avalanche L1 you would like to upgrade. ## Selecting a VM Deployment to Upgrade After starting the Avalanche L1 Upgrade Wizard, you should see something like this: ```bash ? What deployment would you like to upgrade: ▸ Update config for future deployments Existing local deployment ``` If you select the first option, Avalanche-CLI updates your Avalanche L1's config and any future calls to `avalanche blockchain deploy` use the new version you select. However, any existing local deployments continue to use the old version. If you select the second option, the opposite occurs. The existing local deployment switches to the new VM but subsequent deploys use the original. ## Select a VM to Upgrade To The next option asks you to select your new virtual machine. ```bash ? How would you like to update your Avalanche L1's virtual machine: ▸ Update to latest version Update to a specific version Update to a custom binary ``` If you're using the Subnet-EVM, you'll have the option to upgrade to the latest released version. You can also select a specific version or supply a custom binary. If your Avalanche L1 already uses a custom VM, you need to select another custom binary. Once you select your VM, you should see something like: ```bash Upgrade complete. Ready to restart the network. ``` ## Restart the Network If you are running multiple Avalanche L1s concurrently, you may need to update multiple Avalanche L1s to restart the network. All of your deployed must be using the same RPC Protocol version. You can see more details about this [here](/docs/nodes/maintain/upgrade#incompatible-rpc-version-for-custom-vm). Finally, restart the network with: ```bash avalanche network start ``` If the network starts correctly, your Avalanche L1 is now running the upgraded VM. # Authentication (/docs/tooling/avalanche-sdk/chainkit/authentication) --- title: Authentication description: Authentication for the ChainKit SDK icon: Lock --- ### Per-Client Security Schemes This SDK supports the following security scheme globally: | Name | Type | Scheme | | -------- | ------ | ------- | | `apiKey` | apiKey | API key | The ChainKit SDK can be used without an API key, but rate limits will be lower. Adding an API key allows for higher rate limits. To get an API key, create one via [Builder Console](/console/utilities/data-api-keys) and securely store it. Whether or not you use an API key, you can still interact with the SDK effectively, but the API key provides performance benefits for higher request volumes. ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { const result = await avalancheSDK.metrics.healthCheck(); // Handle the result console.log(result); } run(); ``` Never hardcode your API key directly into your code. Instead, securely store it and retrieve it from an environment variable, a secrets manager, or a dedicated configuration storage mechanism. This ensures that sensitive information remains protected and is not exposed in version control or publicly accessible code. # Custom HTTP Client (/docs/tooling/avalanche-sdk/chainkit/custom-http) --- title: Custom HTTP Client description: Custom HTTP Client for the ChainKit SDK icon: Server --- The TypeScript SDK makes API calls using an HTTPClient that wraps the native [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). This client is a thin wrapper around `fetch` and provides the ability to attach hooks around the request lifecycle that can be used to modify the request or handle errors and response. The `HTTPClient` constructor takes an optional `fetcher` argument that can be used to integrate a third-party HTTP client or when writing tests to mock out the HTTP client and feed in fixtures. The following example shows how to use the `beforeRequest` hook to to add a custom header and a timeout to requests and how to use the `requestError` hook to log errors: ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; import { HTTPClient } from "@avalanche-sdk/chainkit/lib/http"; const httpClient = new HTTPClient({ // fetcher takes a function that has the same signature as native `fetch`. fetcher: (request) => { return fetch(request); }, }); httpClient.addHook("beforeRequest", (request) => { const nextRequest = new Request(request, { signal: request.signal || AbortSignal.timeout(5000), }); nextRequest.headers.set("x-custom-header", "custom value"); return nextRequest; }); httpClient.addHook("requestError", (error, request) => { console.group("Request Error"); console.log("Reason:", `${error}`); console.log("Endpoint:", `${request.method} ${request.url}`); console.groupEnd(); }); const sdk = new Avalanche({ httpClient }); ``` # Error Handling (/docs/tooling/avalanche-sdk/chainkit/errors) --- title: Error Handling description: Error Handling for the ChainKit SDK icon: Bug --- All SDK methods return a response object or throw an error. If Error objects are specified in your OpenAPI Spec, the SDK will throw the appropriate Error type. | Error Object | Status Code | Content Type | | :------------------------- | :---------- | :--------------- | | errors.BadRequest | 400 | application/json | | errors.Unauthorized | 401 | application/json | | errors.Forbidden | 403 | application/json | | errors.NotFound | 404 | application/json | | errors.TooManyRequests | 429 | application/json | | errors.InternalServerError | 500 | application/json | | errors.BadGateway | 502 | application/json | | errors.ServiceUnavailable | 503 | application/json | | errors.SDKError | 4xx-5xx | / | Validation errors can also occur when either method arguments or data returned from the server do not match the expected format. The SDKValidationError that is thrown as a result will capture the raw value that failed validation in an attribute called `rawValue`. Additionally, a `pretty()` method is available on this error that can be used to log a nicely formatted string since validation errors can list many issues and the plain error string may be difficult read when debugging. ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; import { BadGateway, BadRequest, Forbidden, InternalServerError, NotFound, SDKValidationError, ServiceUnavailable, TooManyRequests, Unauthorized, } from "@avalanche-sdk/chainkit/models/errors"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { try { await avalancheSDK.data.nfts.reindex({ address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", tokenId: "145", }); } catch (err) { switch (true) { case err instanceof SDKValidationError: { // Validation errors can be pretty-printed console.error(err.pretty()); // Raw value may also be inspected console.error(err.rawValue); return; } case err instanceof BadRequest: { // Handle err.data$: BadRequestData console.error(err); return; } case err instanceof Unauthorized: { // Handle err.data$: UnauthorizedData console.error(err); return; } case err instanceof Forbidden: { // Handle err.data$: ForbiddenData console.error(err); return; } case err instanceof NotFound: { // Handle err.data$: NotFoundData console.error(err); return; } case err instanceof TooManyRequests: { // Handle err.data$: TooManyRequestsData console.error(err); return; } case err instanceof InternalServerError: { // Handle err.data$: InternalServerErrorData console.error(err); return; } case err instanceof BadGateway: { // Handle err.data$: BadGatewayData console.error(err); return; } case err instanceof ServiceUnavailable: { // Handle err.data$: ServiceUnavailableData console.error(err); return; } default: { throw err; } } } } run(); ``` # Getting Started (/docs/tooling/avalanche-sdk/chainkit/getting-started) --- title: Getting Started description: Get started with the ChainKit SDK icon: Rocket --- ### ChainKit SDK The ChainKit SDK provides web3 application developers with multi-chain data related to Avalanche's primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata. **Migration Notice**: This SDK was previously known as the AvaCloud SDK. We have made namespace changes and will discontinue the AvaCloud SDK in favor of the ChainKit SDK. For migration guidance and specific method updates, please refer to the individual method documentation. The SDK is currently available in TypeScript, with more languages coming soon. If you are interested in a language that is not listed, please reach out to us in the [#avalanche-sdk](https://discord.com/channels/578992315641626624/1416238478915665961) channel in the [Avalanche Discord](https://discord.gg/avax). [https://www.npmjs.com/package/@avalanche-sdk/chainkit](https://www.npmjs.com/package/@avalanche-sdk/chainkit) [https://github.com/ava-labs/avalanche-sdk-typescript](https://github.com/ava-labs/avalanche-sdk-typescript) ### SDK Installation ```bash npm install @avalanche-sdk/client ``` ```bash yarn add @avalanche-sdk/client ``` ```bash bun add @avalanche-sdk/client ``` ### SDK Example Usage ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { const result = await avalancheSDK.metrics.healthCheck(); // Handle the result console.log(result); } run(); ``` Refer to the code samples provided for each route to see examples of how to use them in the SDK. Explore routes here [Data API](/docs/api-reference/data-api/getting-started), [Metrics API](/docs/api-reference/metrics-api/getting-started) & [Webhooks API](/docs/api-reference/webhook-api). # Global Parameters (/docs/tooling/avalanche-sdk/chainkit/global-parameters) --- title: Global Parameters description: Global parameters for the ChainKit SDK icon: Globe --- Certain parameters are configured globally. These parameters may be set on the SDK client instance itself during initialization. When configured as an option during SDK initialization, These global values will be used as defaults on the operations that use them. When such operations are called, there is a place in each to override the global value, if needed. For example, you can set `chainId` to `43114` at SDK initialization and then you do not have to pass the same value on calls to operations like getBlock. But if you want to do so you may, which will locally override the global setting. See the example code below for a demonstration. ### Available Globals The following global parameters are available. | Name | Type | Required | Description | | :-------- | :---------------------------- | :------- | :------------------------------------------------------- | | `chainId` | string | No | A supported EVM chain id, chain alias, or blockchain id. | | `network` | components.GlobalParamNetwork | No | A supported network type, either mainnet or a testnet. | Example ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", // Sets chainId globally, will be used if not passed during method call. network: "mainnet", }); async function run() { const result = await avalancheSDK.data.evm.blocks.get({ blockId: "0x17533aeb5193378b9ff441d61728e7a2ebaf10f61fd5310759451627dfca2e7c", chainId: "", // Override the globally set chain id. }); // Handle the result console.log(result); } run(); ``` # Pagination (/docs/tooling/avalanche-sdk/chainkit/pagination) --- title: Pagination description: Pagination for the ChainKit SDK icon: StickyNote --- Some of the endpoints in this SDK support pagination. To use pagination, you make your SDK calls as usual, but the returned response object will also be an async iterable that can be consumed using the `for await...of` syntax. Here's an example of one such pagination call: ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { const result = await avalancheSDK.metrics.chains.list({ network: "mainnet", }); for await (const page of result) { // Handle the page console.log(page); } } run(); ``` # Retries (/docs/tooling/avalanche-sdk/chainkit/retries) --- title: Retries description: Retries for the ChainKit SDK icon: RotateCcw --- Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK. To change the default retry strategy for a single API call, simply provide a retryConfig object to the call: ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { const result = await avalancheSDK.metrics.healthCheck({ retries: { strategy: "backoff", backoff: { initialInterval: 1, maxInterval: 50, exponent: 1.1, maxElapsedTime: 100, }, retryConnectionErrors: false, }, }); // Handle the result console.log(result); } run(); ``` If you'd like to override the default retry strategy for all operations that support retries, you can provide a retryConfig at SDK initialization: ```javascript import { Avalanche } from "@avalanche-sdk/chainkit"; const avalancheSDK = new Avalanche({ retryConfig: { strategy: "backoff", backoff: { initialInterval: 1, maxInterval: 50, exponent: 1.1, maxElapsedTime: 100, }, retryConnectionErrors: false, }, apiKey: "", chainId: "43114", network: "mainnet", }); async function run() { const result = await avalancheSDK.metrics.healthCheck(); // Handle the result console.log(result); } run(); ``` # Client & Transports (/docs/tooling/avalanche-sdk/client/clients-transports) --- title: Client & Transports icon: Plug --- ## Overview Clients provide type-safe interfaces for interacting with Avalanche. Transports handle the communication layer. This separation lets you switch between HTTP, WebSocket, or custom providers without changing your code. The SDK is built on [viem](https://viem.sh), so you get full Ethereum compatibility plus native support for P-Chain, X-Chain, and C-Chain operations. ## Clients Clients are TypeScript interfaces that abstract RPC calls and provide type-safe APIs. ### Avalanche Client (Public Client) The read-only client for querying blockchain data across all Avalanche chains. ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http", }, }); // Access different chains const pChainHeight = await client.pChain.getHeight(); const cChainBalance = await client.getBalance({ address: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", }); ``` ### Avalanche Wallet Client Extends the public client with transaction signing and sending capabilities. ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToWei } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http", }, }); // Send AVAX const hash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: avaxToWei(0.001), }); ``` ### Chain-Specific Clients Access chain-specific operations through sub-clients: - `client.pChain` - Validator operations, staking, subnet management - `client.xChain` - Asset transfers, UTXO operations - `client.cChain` - Atomic transactions - API clients - Admin, Info, Health, Index API, Proposervm operations ## Transports Transports handle data transmission between your application and Avalanche nodes. They abstract RPC protocol implementation. ### HTTP Transport Uses standard HTTP/HTTPS connections. Most common choice for production applications. ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http", // Optional: specify custom URL // url: "https://api.avax.network/ext/bc/C/rpc", }, }); ``` ### WebSocket Transport Maintains a persistent connection for real-time subscriptions and event streaming. ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "ws", // Optional: specify custom WebSocket URL // url: "wss://api.avax.network/ext/bc/C/ws", }, }); ``` ### Custom Transport (EIP-1193) Supports custom transport implementations, including EIP-1193 providers (MetaMask, WalletConnect, etc.). ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; import "@avalanche-sdk/client/window"; // Using window.ethereum (Core, MetaMask, etc.) const walletClient = createAvalancheWalletClient({ account: account, chain: avalanche, transport: { type: "custom", provider: window.ethereum, // Or // provider: window.avalanche, }, }); ``` ## Transport Configuration ### Mainnet/Testnet ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche, avalancheFuji } from "@avalanche-sdk/client/chains"; // Mainnet const mainnetClient = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); // Testnet (Fuji) const testnetClient = createAvalancheClient({ chain: avalancheFuji, transport: { type: "http" }, }); ``` ### Custom Endpoints ```typescript const client = createAvalancheClient({ chain: avalanche, transport: { type: "http", url: "https://your-custom-rpc-endpoint.com/ext/bc/C/rpc", // Optional: Add headers for authentication // fetchOptions: { // headers: { // Authorization: `Bearer ${apiKey}`, // }, // }, }, }); ``` ### Switching Transports Switch between transports without changing your application logic: ```typescript // HTTP client const httpClient = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); // WebSocket client const wsClient = createAvalancheClient({ chain: avalanche, transport: { type: "ws" }, }); // Both have the same API const height1 = await httpClient.pChain.getHeight(); const height2 = await wsClient.pChain.getHeight(); ``` ## Client Selection ### Public Client vs Wallet Client | Feature | Public Client | Wallet Client | | ----------------------- | ------------- | --------------------- | | **Read Operations** | ✅ Yes | ✅ Yes (inherits all) | | **Transaction Signing** | ❌ No | ✅ Yes | | **Transaction Sending** | ❌ No | ✅ Yes | | **Account Required** | ❌ No | ✅ Yes | **Use Public Client for:** Reading blockchain data, querying balances, fetching validator info, reading smart contract state. **Use Wallet Client for:** Sending transactions, signing messages, transferring assets, interacting with smart contracts. ### Chain-Specific Clients The main client provides access to all chains. You can also create standalone chain-specific clients: ```typescript // Main client - access all chains const client = createAvalancheClient({ ... }); await client.pChain.getHeight(); await client.xChain.getBalance({ ... }); // Chain-specific client import { createPChainClient } from "@avalanche-sdk/client"; const pChainOnly = createPChainClient({ ... }); await pChainOnly.getHeight(); ``` **Use chain-specific clients when:** Your app only interacts with one chain, you want smaller bundle size, or need specialized configuration. **Use main client when:** Your app uses multiple chains or you want unified configuration. ## Next Steps - **[Avalanche Client](clients/avalanche-client)** - Read-only operations - **[Avalanche Wallet Client](clients/wallet-client)** - Transaction operations - **[Chain-Specific Clients](clients/p-chain-client)** - P-Chain, X-Chain, and C-Chain clients - **[Public Actions](methods/public-methods/p-chain)** - Read operations reference - **[Wallet Actions](methods/wallet-methods/wallet)** - Write operations reference The SDK follows the same transport patterns as [viem](https://viem.sh/docs/clients/public.html#transport) for compatibility. # Getting Started (/docs/tooling/avalanche-sdk/client/getting-started) --- title: Getting Started icon: Rocket description: Get started with the Avalanche Client SDK - your gateway to building on Avalanche with TypeScript. --- ## Overview The Avalanche Client SDK provides a TypeScript interface for interacting with Avalanche. Built on [viem](https://viem.sh), it offers full Ethereum compatibility plus native support for P-Chain, X-Chain, and C-Chain operations. [https://www.npmjs.com/package/@avalanche-sdk/client](https://www.npmjs.com/package/@avalanche-sdk/client) [https://github.com/ava-labs/avalanche-sdk-typescript](https://github.com/ava-labs/avalanche-sdk-typescript) ## Installation Install the Avalanche Client SDK using your preferred package manager: ```bash npm install @avalanche-sdk/client ``` ```bash yarn add @avalanche-sdk/client ``` ```bash bun add @avalanche-sdk/client ``` ### Requirements - Node.js >= 20.0.0 - TypeScript >= 5.0.0 (recommended) - Modern browsers: Chrome 88+, Firefox 85+, Safari 14+, Edge 88+ ## Quick Start ### Public Client Create a read-only client: ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); // Read operations const pChainHeight = await client.pChain.getHeight(); const balance = await client.getBalance({ address: "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e", }); ``` ### Wallet Client Create a wallet client for transactions: ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToWei } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Send AVAX const hash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: avaxToWei(0.001), }); ``` ## Account Creation ### Private Key ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); ``` ### Mnemonic ```typescript import { mnemonicsToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = mnemonicsToAvalancheAccount("test test test..."); ``` ### HD Key ```typescript import { hdKeyToAvalancheAccount, HDKey } from "@avalanche-sdk/client/accounts"; const hdKey = HDKey.fromMasterSeed(seed); const account = hdKeyToAvalancheAccount(hdKey); ``` [Learn more about accounts →](accounts) ## Transport Configuration ### HTTP ```typescript const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); ``` ### WebSocket ```typescript const client = createAvalancheClient({ chain: avalanche, transport: { type: "ws" }, }); ``` ### Custom Endpoint ```typescript const client = createAvalancheClient({ chain: avalanche, transport: { type: "http", url: "https://your-custom-rpc-endpoint.com/ext/bc/C/rpc", }, }); ``` [Learn more about transports →](clients-transports) ## Avalanche Chains Avalanche has three primary chains: - **P-Chain (Platform Chain)**: Validators, staking, subnets - **X-Chain (Exchange Chain)**: Asset transfers (UTXO model) - **C-Chain (Contract Chain)**: Smart contracts (Ethereum-compatible) ```typescript // P-Chain const validators = await client.pChain.getCurrentValidators({}); // X-Chain const balance = await client.xChain.getBalance({ address: "X-avax1example...", assetID: "AVAX", }); // C-Chain const balance = await client.getBalance({ address: "0x..." }); ``` ## Using viem Features The SDK is built on viem, so you have access to all viem functionality: ```typescript import { formatEther, parseEther } from "@avalanche-sdk/client/utils"; const valueInWei = parseEther("1.0"); const valueInAvax = formatEther(1000000000000000000n); const receipt = await walletClient.waitForTransactionReceipt({ hash }); ``` See the [viem documentation](https://viem.sh/docs/getting-started) for more utilities. ## Next Steps - **[Clients & Transports](clients-transports)** - Understanding clients and transports - **[Account Management](accounts)** - Creating and managing accounts - **[Wallet Operations](methods/wallet-methods/wallet)** - Sending transactions - **[P-Chain Operations](methods/public-methods/p-chain)** - Validator and staking - **[X-Chain Operations](methods/public-methods/x-chain)** - Asset transfers - **[C-Chain Operations](methods/public-methods/c-chain)** - EVM operations ## Need Help? - [Discord community](https://discord.gg/avax) - [GitHub examples](https://github.com/ava-labs/avalanche-sdk-typescript/tree/main/client/examples) - [Open an issue](https://github.com/ava-labs/avalanche-sdk-typescript/issues) # Getting Started (/docs/tooling/avalanche-sdk/interchain/getting-started) --- title: Getting Started description: Install and configure the Interchain SDK icon: rocket --- ## Installation npm install @avalanche-sdk/interchain @avalanche-sdk/client pnpm add @avalanche-sdk/interchain @avalanche-sdk/client yarn add @avalanche-sdk/interchain @avalanche-sdk/client bun add @avalanche-sdk/interchain @avalanche-sdk/client ## Setup ### 1. Create Wallet Client ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalancheFuji } from "@avalanche-sdk/client/chains"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); const wallet = createAvalancheWalletClient({ account, chain: avalancheFuji, transport: { type: "http" }, }); ``` ### 2. Initialize ICM Client ```typescript import { createICMClient } from "@avalanche-sdk/interchain"; import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains"; const icm = createICMClient(wallet, avalancheFuji, dispatch); ``` ## Send Your First Message ```typescript async function sendMessage() { const hash = await icm.sendMsg({ sourceChain: avalancheFuji, destinationChain: dispatch, message: "Hello from Avalanche!", }); console.log("Message sent:", hash); } ``` ## Send Your First Token Transfer ```typescript import { createICTTClient } from "@avalanche-sdk/interchain"; const ictt = createICTTClient(avalancheFuji, dispatch); // Deploy token and contracts (one-time setup) const { contractAddress: tokenAddress } = await ictt.deployERC20Token({ walletClient: wallet, sourceChain: avalancheFuji, name: "My Token", symbol: "MTK", initialSupply: 1000000, }); // Send tokens const { txHash } = await ictt.sendToken({ walletClient: wallet, sourceChain: avalancheFuji, destinationChain: dispatch, tokenHomeContract: "0x...", tokenRemoteContract: "0x...", recipient: "0x...", amountInBaseUnit: 100, }); ``` ## Next Steps - Learn about [Interchain Messaging](/avalanche-sdk/interchain/icm) - Explore [Token Transfers](/avalanche-sdk/interchain/ictt) - Understand [Warp Messages](/avalanche-sdk/interchain/warp) # Interchain SDK (/docs/tooling/avalanche-sdk/interchain) --- title: Interchain SDK icon: network description: Send cross-chain messages and transfer tokens between Avalanche chains and subnets --- ## Overview The Interchain SDK enables cross-chain communication on Avalanche. Send messages and transfer ERC20 tokens between Avalanche C-Chain and subnets using the Teleporter protocol. **Key Features:** - **Interchain Messaging (ICM)** - Send arbitrary messages across chains - **Interchain Token Transfers (ICTT)** - Transfer ERC20 tokens between chains - **Warp Messages** - Parse and build Warp protocol messages - **Type-safe** - Full TypeScript support with IntelliSense ## Quick Start ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { createICMClient } from "@avalanche-sdk/interchain"; import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains"; // Setup wallet const account = privateKeyToAvalancheAccount("0x..."); const wallet = createAvalancheWalletClient({ account, chain: avalancheFuji, transport: { type: "http" }, }); // Create ICM client const icm = createICMClient(wallet); // Send message const hash = await icm.sendMsg({ sourceChain: avalancheFuji, destinationChain: dispatch, message: "Hello from Avalanche!", }); ``` ## What's Next # Account Management (/docs/tooling/avalanche-sdk/client/accounts) --- title: Account Management icon: users description: Learn how to create and manage accounts in the Avalanche Client SDK with support for EVM, X-Chain, and P-Chain operations. --- ## Overview Avalanche accounts work across all three chains—P-Chain, X-Chain, and C-Chain—with a single account. Each account provides both EVM addresses (for C-Chain) and XP addresses (for X/P-Chain), so you can interact with the entire Avalanche network without managing separate accounts. ## Account Structure Every Avalanche account has an EVM account for C-Chain and an optional XP account for X/P-Chain: ```typescript type AvalancheAccount = { evmAccount: Account; // C-Chain xpAccount?: XPAccount; // X/P-Chain getEVMAddress: () => Address; getXPAddress: (chain?: "X" | "P" | "C", hrp?: string) => XPAddress; }; ``` ### Quick Start ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Get addresses for all chains const evmAddress = account.getEVMAddress(); // 0x742d35Cc... const xChainAddress = account.getXPAddress("X"); // X-avax1... const pChainAddress = account.getXPAddress("P"); // P-avax1... ``` ## Account Types ### Local Accounts Local accounts store keys on your machine and sign transactions before broadcasting. Use these for server-side apps, bots, or when you need full control. - [Private Key Accounts](accounts/local/private-key) - Simple and direct - [Mnemonic Accounts](accounts/local/mnemonic) - Easy recovery with seed phrases - [HD Key Accounts](accounts/local/hd-key) - Advanced key derivation ### JSON-RPC Accounts JSON-RPC accounts use external wallets (MetaMask, Core, etc.) for signing. Perfect for browser-based dApps where users control their own keys. [Learn more about JSON-RPC accounts →](accounts/json-rpc) ## Working with Accounts ### EVM Account The `evmAccount` handles all C-Chain operations—smart contracts, ERC-20 transfers, and standard EVM interactions. ```typescript const evmAccount = account.evmAccount; console.log(evmAccount.address); // 0x742d35Cc... ``` ### XP Account The `xpAccount` handles X-Chain and P-Chain operations—UTXO transactions, asset transfers, and staking. ```typescript if (account.xpAccount) { const xpAccount = account.xpAccount; console.log(xpAccount.publicKey); } ``` ### Getting Addresses ```typescript // C-Chain address const evmAddress = account.getEVMAddress(); // 0x742d35Cc... // X/P-Chain addresses const xChainAddress = account.getXPAddress("X"); // X-avax1... const pChainAddress = account.getXPAddress("P"); // P-avax1... // Network-specific (mainnet vs testnet) const mainnet = account.getXPAddress("X", "avax"); const testnet = account.getXPAddress("X", "fuji"); ``` ## Creating Accounts ### Private Key ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); ``` [Private Key Accounts →](accounts/local/private-key) ### Mnemonic ```typescript import { mnemonicsToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = mnemonicsToAvalancheAccount("abandon abandon abandon..."); ``` [Mnemonic Accounts →](accounts/local/mnemonic) ### HD Key ```typescript import { hdKeyToAvalancheAccount, HDKey } from "@avalanche-sdk/client/accounts"; const hdKey = HDKey.fromMasterSeed(seed); const account = hdKeyToAvalancheAccount(hdKey, { accountIndex: 0 }); ``` [HD Key Accounts →](accounts/local/hd-key) ## Address Formats - **C-Chain:** `0x...` (Ethereum-compatible) - **X/P-Chain:** `avax1...` or `X-avax1...` / `P-avax1...` (Bech32-encoded) [Network-Specific Addresses →](accounts/local/addresses) ## Security **Never expose private keys or mnemonics in client-side code or commit them to version control. Use environment variables.** ```typescript // ✅ Good const account = privateKeyToAvalancheAccount(process.env.PRIVATE_KEY!); // ❌ Bad const account = privateKeyToAvalancheAccount("0x1234..."); ``` ## Comparison Table | Feature | Private Key | Mnemonic | HD Key | JSON-RPC | | ----------------- | ----------- | --------- | -------- | ------------ | | **Ease of Use** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | **Recovery** | ❌ | ✅ | ✅ | ❌ | | **Multi-Account** | ❌ | ✅ | ✅ | ❌ | | **Security** | ⚠️ High | ✅ High | ✅ High | ✅ Very High | | **Use Case** | Server/Bots | User Apps | Advanced | User Apps | ## Next Steps - [Private Key Accounts](accounts/local/private-key) - Simple and direct - [Mnemonic Accounts](accounts/local/mnemonic) - Easy recovery - [HD Key Accounts](accounts/local/hd-key) - Advanced key derivation - [JSON-RPC Accounts](accounts/json-rpc) - Browser wallet integration - [Account Utilities](accounts/local/utilities) - Helper functions - [Wallet Operations](methods/wallet-methods/wallet) - Send transactions # API Clients (/docs/tooling/avalanche-sdk/client/clients/api-clients) --- title: API Clients --- ## Overview API clients provide access to node-level operations. They're included with the main Avalanche Client and handle administrative tasks, node information, health monitoring, and indexed blockchain queries. ## Accessing from Avalanche Client All API clients are available on the main Avalanche Client: ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); // Admin API - Node configuration and profiling const admin = client.admin; // Info API - Node and network information const info = client.info; // Health API - Node health monitoring const health = client.health; // ProposerVM API - ProposerVM operations per chain const proposervmPChain = client.proposerVM.pChain; const proposervmXChain = client.proposerVM.xChain; const proposervmCChain = client.proposerVM.cChain; // Index API - Indexed blockchain queries const indexPChainBlock = client.indexBlock.pChain; const indexCChainBlock = client.indexBlock.cChain; const indexXChainBlock = client.indexBlock.xChain; const indexXChainTx = client.indexTx.xChain; ``` ## Admin API Client Node configuration, aliases, logging, and profiling. ### From Avalanche Client ```typescript const admin = client.admin; // Example: Set logger level await admin.setLoggerLevel({ loggerName: "C", logLevel: "DEBUG", }); ``` ### Create Standalone Client ```typescript import { createAdminApiClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const adminClient = createAdminApiClient({ chain: avalanche, transport: { type: "http", url: "https://api.avax.network/ext/admin", }, }); await adminClient.alias({ endpoint: "bc/X", alias: "myAlias", }); ``` [View all Admin API methods →](methods/public-methods/api#admin-api-client) ## Info API Client Node and network information, statistics, and status. ### From Avalanche Client ```typescript const info = client.info; // Example: Get network info const networkID = await info.getNetworkID(); const version = await info.getNodeVersion(); ``` ### Create Standalone Client ```typescript import { createInfoApiClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const infoClient = createInfoApiClient({ chain: avalanche, transport: { type: "http" }, }); const networkID = await infoClient.getNetworkID(); const version = await infoClient.getNodeVersion(); ``` [View all Info API methods →](methods/public-methods/api#info-api-client) ## Health API Client Node health monitoring and status checks. ### From Avalanche Client ```typescript const health = client.health; // Example: Check node health const status = await health.health({}); const isAlive = await health.liveness(); ``` ### Create Standalone Client ```typescript import { createHealthApiClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const healthClient = createHealthApiClient({ chain: avalanche, transport: { type: "http" }, }); const health = await healthClient.health({}); const liveness = await healthClient.liveness(); ``` [View all Health API methods →](methods/public-methods/api#health-api-client) ## ProposerVM API Client ProposerVM operations for each chain. ### From Avalanche Client ```typescript // Access ProposerVM for each chain const proposervmPChain = client.proposerVM.pChain; const proposervmXChain = client.proposerVM.xChain; const proposervmCChain = client.proposerVM.cChain; ``` ### Create Standalone Client ```typescript import { createProposervmApiClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; // P-Chain ProposerVM const proposervmPChain = createProposervmApiClient({ chain: avalanche, transport: { type: "http" }, clientType: "proposervmPChain", }); // X-Chain ProposerVM const proposervmXChain = createProposervmApiClient({ chain: avalanche, transport: { type: "http" }, clientType: "proposervmXChain", }); // C-Chain ProposerVM const proposervmCChain = createProposervmApiClient({ chain: avalanche, transport: { type: "http" }, clientType: "proposervmCChain", }); // Example: Get proposed height const pChainHeight = await proposervmPChain.getProposedHeight(); ``` ## Index API Clients Fast indexed queries for blockchain data. ### From Avalanche Client ```typescript // Block indexes const indexPChainBlock = client.indexBlock.pChain; const indexCChainBlock = client.indexBlock.cChain; const indexXChainBlock = client.indexBlock.xChain; // Transaction index const indexXChainTx = client.indexTx.xChain; // Example: Get last accepted block const lastBlock = await indexPChainBlock.getLastAccepted({ encoding: "hex", }); ``` ### Create Standalone Client ```typescript import { createIndexApiClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; // P-Chain block index const indexPChainBlock = createIndexApiClient({ chain: avalanche, transport: { type: "http" }, clientType: "indexPChainBlock", }); // C-Chain block index const indexCChainBlock = createIndexApiClient({ chain: avalanche, transport: { type: "http" }, clientType: "indexCChainBlock", }); // X-Chain block index const indexXChainBlock = createIndexApiClient({ chain: avalanche, transport: { type: "http" }, clientType: "indexXChainBlock", }); // X-Chain transaction index const indexXChainTx = createIndexApiClient({ chain: avalanche, transport: { type: "http" }, clientType: "indexXChainTx", }); // Example: Get container by index const block = await indexPChainBlock.getContainerByIndex({ index: 12345, encoding: "hex", }); ``` [View all Index API methods →](methods/public-methods/api#index-api-clients) ## Quick Examples ### Node Health Check ```typescript const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const health = await client.health.health({}); const liveness = await client.health.liveness(); console.log("Node healthy:", health.healthy); ``` ### Get Node Information ```typescript const version = await client.info.getNodeVersion(); const networkID = await client.info.getNetworkID(); const nodeID = await client.info.getNodeID(); console.log(`Node ${nodeID.nodeID} v${version} on network ${networkID}`); ``` ### Query Indexed Blocks ```typescript const lastBlock = await client.indexBlock.pChain.getLastAccepted({ encoding: "hex", }); const block = await client.indexBlock.cChain.getContainerByIndex({ index: 12345, encoding: "hex", }); ``` ## When to Use - **Admin API**: Node configuration, profiling, logging (requires admin access) - **Info API**: Node and network information - **Health API**: Health monitoring and status checks - **Index API**: Fast indexed queries for blocks and transactions - **ProposerVM API**: ProposerVM operations per chain **Note:** Admin API operations require administrative access and may not be available on public endpoints. ## Next Steps - **[API Methods Reference](methods/public-methods/api)** - Complete method documentation - **[Avalanche Client](clients/avalanche-client)** - Main client operations - **[Wallet Client](clients/wallet-client)** - Transaction operations # Avalanche Client (/docs/tooling/avalanche-sdk/client/clients/avalanche-client) --- title: Avalanche Client --- ## Overview The Avalanche Client (also known as the Public Client) is the main client for read-only operations across all Avalanche chains. It provides a unified interface for querying data from P-Chain, X-Chain, C-Chain, and various API endpoints. **When to use:** Use the Avalanche Client when you need to query blockchain data but don't need to send transactions. ## Installation & Setup For setup instructions, see the [Getting Started](/avalanche-sdk/client-sdk/getting-started) guide. ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); ``` ## Available Clients The Avalanche Client automatically provides access to all chain-specific and API clients: ```typescript // Chain clients client.pChain; // P-Chain operations (validators, staking, subnets) client.xChain; // X-Chain operations (assets, UTXOs) client.cChain; // C-Chain operations (EVM, atomic transactions) // API clients client.admin; // Admin API operations client.info; // Info API operations client.health; // Health API operations client.proposerVM.pChain; // ProposerVM API for P Chain client.proposerVM.xChain; // ProposerVM API for X Chain client.proposerVM.cChain; // ProposerVM API for C Chain client.indexBlock.pChain; // P-Chain block index client.indexBlock.cChain; // C-Chain block index client.indexBlock.xChain; // X-Chain block index client.indexTx.xChain; // X-Chain transaction index ``` ## Available Methods The Avalanche Client extends viem's Public Client and provides additional Avalanche-specific methods: ### Avalanche-Specific Methods - **Public Methods**: `baseFee`, `getChainConfig`, `maxPriorityFeePerGas`, `feeConfig`, `getActiveRulesAt` For complete documentation, see [Public Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/public). ### Chain-Specific Methods Access methods through chain clients: - **P-Chain Methods**: See [P-Chain Client](/avalanche-sdk/client-sdk/clients/p-chain-client) and [P-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/p-chain) - **X-Chain Methods**: See [X-Chain Client](/avalanche-sdk/client-sdk/clients/x-chain-client) and [X-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/x-chain) - **C-Chain Methods**: See [C-Chain Client](/avalanche-sdk/client-sdk/clients/c-chain-client) and [C-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/c-chain) ### viem Public Client Methods The client extends viem's Public Client, providing access to all standard EVM actions: - `getBalance`, `getBlock`, `getBlockNumber`, `getTransaction`, `getTransactionReceipt` - `readContract`, `call`, `estimateGas`, `getCode`, `getStorageAt` - And many more... See the [viem documentation](https://viem.sh/docs/getting-started) for all available EVM actions. ## Common Operations ### Query P-Chain Data ```typescript // Get current block height const height = await client.pChain.getHeight(); // Get current validators const validators = await client.pChain.getCurrentValidators({ subnetID: "11111111111111111111111111111111LpoYY", }); // Get subnet information const subnet = await client.pChain.getSubnet({ subnetID: "11111111111111111111111111111111LpoYY", }); // Get balance const balance = await client.pChain.getBalance({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], }); ``` ### Query X-Chain Data ```typescript // Get balance for specific asset const balance = await client.xChain.getBalance({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], assetID: "AVAX", }); // Get all balances const allBalances = await client.xChain.getAllBalances({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], }); // Get asset information const asset = await client.xChain.getAssetDescription({ assetID: "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", }); ``` ### Query C-Chain Data ```typescript // Get EVM balance (viem action) const balance = await client.getBalance({ address: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", }); // Get transaction receipt (viem action) const receipt = await client.getTransactionReceipt({ hash: "0x...", }); // Get base fee (Avalanche-specific) const baseFee = await client.baseFee(); // Get chain config const chainConfig = await client.getChainConfig(); // Get atomic transaction const atomicTx = await client.cChain.getAtomicTx({ txID: "2QouvMUbQ6oy7yQ9tLvL3L8tGQG2QK1wJ1q1wJ1q1wJ1q1wJ1q1wJ1q1wJ1", }); ``` ### Query API Data ```typescript // Admin API - Get peers const peers = await client.admin.getPeers(); // Info API - Get node version const version = await client.info.getNodeVersion(); // Health API - Get health status const health = await client.health.health(); // Index API - Get block by index const block = await client.indexPChainBlock.getContainerByIndex({ index: 12345, }); ``` ## Error Handling Always handle errors appropriately: ```typescript import { BaseError } from "viem"; try { const balance = await client.getBalance({ address: "0x...", }); } catch (error) { if (error instanceof BaseError) { console.error("RPC Error:", error.message); } else { console.error("Unknown error:", error); } } ``` ## When to Use This Client - ✅ Querying blockchain data - ✅ Reading balances and transaction history - ✅ Checking validator information - ✅ Monitoring network status - ✅ Inspecting smart contract state **Don't use this client for:** - ❌ Sending transactions (use [Wallet Client](/avalanche-sdk/client-sdk/clients/wallet-client)) - ❌ Signing messages (use [Wallet Client](/avalanche-sdk/client-sdk/clients/wallet-client)) - ❌ Cross-chain transfers (use [Wallet Client](/avalanche-sdk/client-sdk/clients/wallet-client)) ## Best Practices ### Use Specific Clients ```typescript // Good: Use P-Chain client for platform operations const validators = await client.pChain.getCurrentValidators({}); // Good: Use X-Chain client for asset operations const balance = await client.xChain.getBalance({ addresses: ["X-avax..."], assetID: "AVAX", }); // Good: Use C-Chain client for EVM operations const atomicTx = await client.cChain.getAtomicTx({ txID: "0x...", }); ``` ### Using viem Actions Since the Avalanche Client extends viem's Public Client, you have access to all viem actions: ```typescript // Use viem's readContract action const result = await client.readContract({ address: "0x...", abi: contractABI, functionName: "balanceOf", args: ["0x..."], }); // Use viem's getTransaction action const tx = await client.getTransaction({ hash: "0x...", }); // Use viem's estimateGas action const gas = await client.estimateGas({ to: "0x...", value: parseEther("0.001"), }); ``` See the [viem documentation](https://viem.sh/docs/getting-started) for all available actions. ## Next Steps - **[Wallet Client](/avalanche-sdk/client-sdk/clients/wallet-client)** - Transaction signing and sending - **[P-Chain Client](/avalanche-sdk/client-sdk/clients/p-chain-client)** - Detailed P-Chain operations - **[X-Chain Client](/avalanche-sdk/client-sdk/clients/x-chain-client)** - Asset and UTXO operations - **[C-Chain Client](/avalanche-sdk/client-sdk/clients/c-chain-client)** - EVM and atomic operations - **[Public Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/public)** - Complete public method documentation # C-Chain Client (/docs/tooling/avalanche-sdk/client/clients/c-chain-client) --- title: C-Chain Client --- ## Overview The C-Chain (Contract Chain) Client provides an interface for interacting with Avalanche's Contract Chain, which is an instance of the Ethereum Virtual Machine (EVM) with additional Avalanche-specific features like cross-chain atomic transactions. **When to use:** Use the C-Chain Client for EVM operations and atomic transactions (cross-chain transfers). ## Installation & Setup For setup instructions, see the [Getting Started](/avalanche-sdk/client-sdk/getting-started) guide. ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const cChainClient = client.cChain; ``` Or create a standalone C-Chain client: ```typescript import { createCChainClient } from "@avalanche-sdk/client"; const cChainClient = createCChainClient({ chain: avalanche, transport: { type: "http" }, }); ``` ## Available Methods The C-Chain Client provides methods for: - **Atomic Transaction Operations**: `getAtomicTx`, `getAtomicTxStatus` - **UTXO Operations**: `getUTXOs` - **Transaction Operations**: `issueTx` Additionally, the C-Chain Client extends viem's Public Client, providing access to all standard EVM actions such as `getBalance`, `getBlock`, `readContract`, `call`, and more. For complete method documentation with signatures, parameters, and examples, see the [C-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/c-chain). ## Common Use Cases ### Query Atomic Transactions ```typescript // Get atomic transaction details const atomicTx = await client.cChain.getAtomicTx({ txID: "2QouvMUbQ6oy7yQ9tLvL3L8tGQG2QK1wJ1q1wJ1q1wJ1q1wJ1q1wJ1q1wJ1", }); console.log("Source chain:", atomicTx.sourceChain); console.log("Destination chain:", atomicTx.destinationChain); console.log("Transfers:", atomicTx.transfers); // Get atomic transaction status const status = await client.cChain.getAtomicTxStatus({ txID: "2QouvMUbQ6oy7yQ9tLvL3L8tGQG2QK1wJ1q1wJ1q1wJ1q1wJ1q1wJ1q1wJ1", }); console.log("Status:", status.status); ``` ### Query UTXOs ```typescript // Get UTXOs for C-Chain addresses const utxos = await client.cChain.getUTXOs({ addresses: ["0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6"], limit: 100, }); console.log("Number of UTXOs:", utxos.utxos.length); ``` ## Using viem Actions The C-Chain Client extends viem's Public Client, so you have access to all standard EVM actions: ```typescript // Get EVM balance const balance = await client.getBalance({ address: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", }); // Get transaction receipt const receipt = await client.getTransactionReceipt({ hash: "0x...", }); // Get block number const blockNumber = await client.getBlockNumber(); // Read smart contract const result = await client.readContract({ address: "0x...", abi: contractABI, functionName: "balanceOf", args: ["0x..."], }); // Get block information const block = await client.getBlock({ blockNumber: blockNumber, }); ``` See the [viem documentation](https://viem.sh/docs/getting-started) for all available EVM actions. ## Wallet Operations For transaction operations (sending transactions, writing contracts), use the wallet client: ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { parseEther } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Send AVAX const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", value: parseEther("0.001"), }); console.log("Transaction hash:", txHash); ``` ### Cross-Chain Operations ```typescript // Export from C-Chain to P-Chain const exportTx = await walletClient.cChain.prepareExportTxn({ destinationChain: "P", to: account.getXPAddress("P"), amount: "0.001", }); const exportTxHash = await walletClient.sendXPTransaction(exportTx); console.log("Export transaction:", exportTxHash); // Import to C-Chain from P-Chain const importTx = await walletClient.cChain.prepareImportTxn({ to: account.getEVMAddress(), amount: "0.001", sourceChain: "P", }); const importTxHash = await walletClient.sendXPTransaction(importTx); console.log("Import transaction:", importTxHash); ``` For complete wallet operations documentation, see [C-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/c-chain-wallet). ## Next Steps - **[C-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/c-chain)** - Complete method documentation - **[C-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/c-chain-wallet)** - Transaction preparation and sending - **[Wallet Client](/avalanche-sdk/client-sdk/clients/wallet-client)** - Complete wallet operations - **[P-Chain Client](/avalanche-sdk/client-sdk/clients/p-chain-client)** - Validator operations - **[X-Chain Client](/avalanche-sdk/client-sdk/clients/x-chain-client)** - Asset operations # Clients (/docs/tooling/avalanche-sdk/client/clients) --- title: Clients --- ## Overview The SDK provides different client types for interacting with Avalanche. Each client is optimized for specific use cases. ## Client Architecture ```typescript Avalanche Client (Public) ├── P-Chain Client ├── X-Chain Client ├── C-Chain Client ├── Admin API Client ├── Info API Client ├── Health API Client ├── ProposerVM Client └── Index API Clients Avalanche Wallet Client ├── All Public Client Methods ├── P-Chain Wallet Operations ├── X-Chain Wallet Operations ├── C-Chain Wallet Operations └── ERC20 Token Operations ``` ## Client Types ### Main Clients - **[Avalanche Client](clients/avalanche-client)** - Read-only operations for all chains - **[Avalanche Wallet Client](clients/wallet-client)** - Transaction signing and sending ### Chain-Specific Clients - **[P-Chain Client](clients/p-chain-client)** - Validator and staking operations - **[X-Chain Client](clients/x-chain-client)** - Asset transfers and UTXO operations - **[C-Chain Client](clients/c-chain-client)** - EVM and atomic transaction operations ### API Clients - **[Admin API Client](clients/api-clients#admin-api-client)** - Administrative node operations - **[Info API Client](clients/api-clients#info-api-client)** - Node information and network statistics - **[Health API Client](clients/api-clients#health-api-client)** - Node health monitoring - **[ProposerVM Client](clients/api-clients#proposervm-client)** - ProposerVM operations - **[Index API Clients](clients/api-clients#index-api-clients)** - Indexed blockchain data queries ## Configuration All clients accept a common configuration: ```typescript interface AvalancheClientConfig { transport: Transport; // Required: HTTP, WebSocket, or Custom chain?: Chain; // Optional: Network configuration account?: Account | Address; // Optional: For wallet operations apiKey?: string; // Optional: For authenticated endpoints rlToken?: string; // Optional: Rate limit token key?: string; // Optional: Client key identifier name?: string; // Optional: Client name pollingInterval?: number; // Optional: Polling interval in ms (default: chain.blockTime / 3) cacheTime?: number; // Optional: Cache time in ms (default: chain.blockTime / 3) batch?: { multicall?: boolean | MulticallBatchOptions }; // Optional: Batch settings ccipRead?: | { request?: ( params: CcipRequestParameters ) => Promise; } | false; // Optional: CCIP Read config experimental_blockTag?: BlockTag; // Optional: Default block tag (default: 'latest') rpcSchema?: RpcSchema; // Optional: Typed JSON-RPC schema type?: string; // Optional: Client type } ``` ### Configuration Options | Option | Type | Required | Default | Description | | ----------------- | -------------------- | -------- | --------------------- | ------------------------------------------------- | | `transport` | `Transport` | ✅ Yes | - | Transport configuration (HTTP, WebSocket, Custom) | | `chain` | `Chain` | No | - | Network configuration (mainnet/testnet) | | `account` | `Account \| Address` | No | - | Account for signing operations | | `apiKey` | `string` | No | - | API key for authenticated endpoints | | `rlToken` | `string` | No | - | Rate limit token | | `key` | `string` | No | - | Client key identifier | | `name` | `string` | No | - | Client name | | `pollingInterval` | `number` | No | `chain.blockTime / 3` | Polling interval in milliseconds | | `cacheTime` | `number` | No | `chain.blockTime / 3` | Cache time in milliseconds | | `batch` | `object` | No | - | Batch settings (multicall configuration) | | `ccipRead` | `object \| false` | No | - | CCIP Read configuration | | `rpcSchema` | `RpcSchema` | No | - | Typed JSON-RPC schema | | `type` | `string` | No | - | Client type identifier | ## Usage Examples ### Public Client ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); // Read data from all chains const pHeight = await client.pChain.getHeight(); const balance = await client.getBalance({ address: "0x..." }); ``` ### Wallet Client ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToWei } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Send transaction const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: avaxToWei(0.001), }); ``` ### Accessing Sub-Clients ```typescript const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); // Chain clients client.pChain; // P-Chain operations client.xChain; // X-Chain operations client.cChain; // C-Chain operations // API clients client.admin; // Admin API client.info; // Info API client.health; // Health API client.proposerVM.pChain; // ProposerVM API for P Chain client.proposerVM.xChain; // ProposerVM API for X Chain client.proposerVM.cChain; // ProposerVM API for C Chain client.indexBlock.pChain; // P-Chain block index client.indexBlock.cChain; // C-Chain block index client.indexBlock.xChain; // X-Chain block index client.indexTx.xChain; // X-Chain transaction index ``` ## Next Steps - **[Avalanche Client](clients/avalanche-client)** - Read-only operations - **[Avalanche Wallet Client](clients/wallet-client)** - Transaction operations - **[Chain-Specific Clients](clients/p-chain-client)** - P, X, and C-Chain clients - **[API Clients](clients/api-clients)** - Admin, Info, Health, ProposerVM, and Index APIs # P-Chain Client (/docs/tooling/avalanche-sdk/client/clients/p-chain-client) --- title: P-Chain Client --- ## Overview The P-Chain (Platform Chain) Client provides an interface for interacting with Avalanche's Platform Chain, which is responsible for coordinating validators, managing subnets, creating blockchains, and handling staking operations. **When to use:** Use the P-Chain Client for validator operations, staking, subnet management, and blockchain creation. ## Installation & Setup For setup instructions, see the [Getting Started](/avalanche-sdk/client-sdk/getting-started) guide. ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const pChainClient = client.pChain; ``` Or create a standalone P-Chain client: ```typescript import { createPChainClient } from "@avalanche-sdk/client"; const pChainClient = createPChainClient({ chain: avalanche, transport: { type: "http" }, }); ``` ## Available Methods The P-Chain Client provides methods for: - **Balance Operations**: `getBalance`, `getUTXOs` - **Validator Operations**: `getCurrentValidators`, `getValidatorsAt`, `sampleValidators`, `getL1Validator` - **Staking Operations**: `getStake`, `getTotalStake`, `getMinStake` - **Subnet Operations**: `getSubnet`, `getSubnets`, `getStakingAssetID` - **Blockchain Operations**: `getBlockchains`, `getBlockchainStatus`, `validatedBy`, `validates` - **Block Operations**: `getHeight`, `getBlock`, `getBlockByHeight`, `getProposedHeight`, `getTimestamp` - **Transaction Operations**: `getTx`, `getTxStatus`, `issueTx` - **Fee Operations**: `getFeeConfig`, `getFeeState` - **Supply Operations**: `getCurrentSupply` - **Reward Operations**: `getRewardUTXOs` For complete method documentation with signatures, parameters, and examples, see the [P-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/p-chain). ## Common Use Cases ### Query Validators ```typescript // Get current validators const validators = await client.pChain.getCurrentValidators({}); console.log("Total validators:", validators.validators.length); // Get validators at specific height const validatorsAt = await client.pChain.getValidatorsAt({ height: 1000001, subnetID: "11111111111111111111111111111111LpoYY", }); ``` ### Query Staking Information ```typescript // Get minimum stake requirements const minStake = await client.pChain.getMinStake({ subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Min validator stake:", minStake.minValidatorStake); console.log("Min delegator stake:", minStake.minDelegatorStake); // Get total stake for a subnet const totalStake = await client.pChain.getTotalStake({ subnetID: "11111111111111111111111111111111LpoYY", }); // Get stake for specific addresses const stake = await client.pChain.getStake({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], subnetID: "11111111111111111111111111111111LpoYY", }); ``` ### Query Subnet Information ```typescript // Get subnet information const subnet = await client.pChain.getSubnet({ subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Is permissioned:", subnet.isPermissioned); console.log("Control keys:", subnet.controlKeys); // Get all blockchains in the network const blockchains = await client.pChain.getBlockchains(); // Get blockchains validated by a subnet const validatedBlockchains = await client.pChain.validates({ subnetID: "11111111111111111111111111111111LpoYY", }); ``` ### Query Balance and UTXOs ```typescript // Get balance for addresses const balance = await client.pChain.getBalance({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], }); console.log("Total balance:", balance.balance); console.log("Unlocked:", balance.unlocked); // Get UTXOs const utxos = await client.pChain.getUTXOs({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], limit: 100, }); ``` ### Query Fee Information ```typescript // Get fee configuration const feeConfig = await client.pChain.getFeeConfig(); console.log("Fee weights:", feeConfig.weights); console.log("Min price:", feeConfig.minPrice); // Get current fee state const feeState = await client.pChain.getFeeState(); console.log("Current fee price:", feeState.price); console.log("Fee capacity:", feeState.capacity); ``` ## Wallet Operations For transaction operations (preparing and sending transactions), use the wallet client: ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Prepare and send base transaction const baseTxn = await walletClient.pChain.prepareBaseTxn({ outputs: [ { addresses: [account.getXPAddress("P")], amount: 0.00001, }, ], }); const txID = await walletClient.sendXPTransaction(baseTxn); console.log("Transaction sent:", txID); ``` For complete wallet operations documentation, see [P-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/p-chain-wallet). ## Next Steps - **[P-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/p-chain)** - Complete method documentation - **[P-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/p-chain-wallet)** - Transaction preparation and signing - **[Wallet Client](/avalanche-sdk/client-sdk/clients/wallet-client)** - Complete wallet operations - **[X-Chain Client](/avalanche-sdk/client-sdk/clients/x-chain-client)** - Asset transfers - **[C-Chain Client](/avalanche-sdk/client-sdk/clients/c-chain-client)** - EVM operations # Avalanche Wallet Client (/docs/tooling/avalanche-sdk/client/clients/wallet-client) --- title: Avalanche Wallet Client --- ## Overview The Avalanche Wallet Client extends the Public Client with full transaction signing and sending capabilities. It enables cross-chain operations, atomic transactions, and comprehensive wallet management across all Avalanche chains. **When to use:** Use the Wallet Client when you need to sign and send transactions, sign messages, or manage accounts. ## Installation & Setup For setup instructions, see the [Getting Started](/avalanche-sdk/client-sdk/getting-started) guide. ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, // Hoist the account, otherwise we can pass a custom provider for injected provider or pass a account for each method chain: avalanche, transport: { type: "http" }, }); ``` ## Available Wallet Operations The Wallet Client provides access to: ```typescript // Chain wallet operations walletClient.pChain; // P-Chain wallet operations walletClient.xChain; // X-Chain wallet operations walletClient.cChain; // C-Chain wallet operations // Core wallet methods walletClient.send(); // Send transactions walletClient.sendXPTransaction(); // Send XP transactions walletClient.signXPMessage(); // Sign XP messages walletClient.signXPTransaction(); // Sign XP transactions walletClient.waitForTxn(); // Wait for transaction confirmation walletClient.getAccountPubKey(); // Get account public key ``` For complete method documentation, see: - **[Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/wallet)** - Core wallet operations - **[P-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/p-chain-wallet)** - P-Chain transactions - **[X-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/x-chain-wallet)** - X-Chain transactions - **[C-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/c-chain-wallet)** - C-Chain transactions ## Common Operations ### Send AVAX on C-Chain ```typescript import { avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const hash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", value: avaxToNanoAvax(0.001), // 0.001 AVAX }); console.log("Transaction hash:", hash); ``` ### P-Chain Wallet Operations ```typescript // Prepare and send base transaction const baseTxn = await walletClient.pChain.prepareBaseTxn({ outputs: [ { addresses: [account.getXPAddress("P")], amount: avaxToNanoAvax(0.00001), }, ], }); const txID = await walletClient.sendXPTransaction(baseTxn); console.log("P-Chain transaction:", txID); ``` ### X-Chain Wallet Operations ```typescript // Prepare and send base transaction const xChainTx = await walletClient.xChain.prepareBaseTxn({ outputs: [ { addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], amount: avaxToNanoAvax(1), // 1 AVAX }, ], }); const txID = await walletClient.sendXPTransaction(xChainTx); console.log("X-Chain transaction:", txID); ``` ### Sign Messages ```typescript // Sign XP message const signedMessage = await walletClient.signXPMessage({ message: "Hello Avalanche", }); console.log("Signed message:", signedMessage); ``` ### Wait for Transaction Confirmation ```typescript try { await walletClient.waitForTxn({ txID: "0x...", chainAlias: "P", }); console.log("Transaction confirmed!"); } catch (error) { console.error("chain confirmation failed:", error); } ``` ## When to Use This Client - ✅ Sending transactions - ✅ Signing messages and transactions - ✅ Cross-chain transfers - ✅ Managing accounts - ✅ All wallet operations ## Next Steps - **[Wallet Methods Reference](/avalanche-sdk/client-sdk/methods/wallet-methods/wallet)** - Complete wallet method documentation - **[P-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/p-chain-wallet)** - P-Chain transaction operations - **[X-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/x-chain-wallet)** - X-Chain transaction operations - **[C-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/c-chain-wallet)** - C-Chain transaction operations - **[Account Management](/avalanche-sdk/client-sdk/accounts)** - Account types and management # X-Chain Client (/docs/tooling/avalanche-sdk/client/clients/x-chain-client) --- title: X-Chain Client --- ## Overview The X-Chain (Exchange Chain) Client provides an interface for interacting with Avalanche's Exchange Chain, which handles asset creation, trading, transfers, and UTXO management. **When to use:** Use the X-Chain Client for asset operations, UTXO management, and X-Chain transaction queries. ## Installation & Setup For setup instructions, see the [Getting Started](/avalanche-sdk/client-sdk/getting-started) guide. ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const xChainClient = client.xChain; ``` Or create a standalone X-Chain client: ```typescript import { createXChainClient } from "@avalanche-sdk/client"; const xChainClient = createXChainClient({ chain: avalanche, transport: { type: "http" }, }); ``` ## Available Methods The X-Chain Client provides methods for: - **Balance Operations**: `getBalance`, `getAllBalances` - **Asset Operations**: `getAssetDescription`, `buildGenesis` - **UTXO Operations**: `getUTXOs` - **Block Operations**: `getHeight`, `getBlock`, `getBlockByHeight` - **Transaction Operations**: `getTx`, `getTxStatus`, `getTxFee`, `issueTx` For complete method documentation with signatures, parameters, and examples, see the [X-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/x-chain). ## Common Use Cases ### Query Balances ```typescript // Get balance for specific asset const balance = await client.xChain.getBalance({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], assetID: "AVAX", }); console.log("Balance:", balance.balance); // Get all balances for all assets const allBalances = await client.xChain.getAllBalances({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], }); console.log("All balances:", allBalances.balances); ``` ### Query Asset Information ```typescript // Get asset description const asset = await client.xChain.getAssetDescription({ assetID: "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", }); console.log("Asset name:", asset.name); console.log("Asset symbol:", asset.symbol); console.log("Denomination:", asset.denomination); ``` ### Query UTXOs ```typescript // Get UTXOs for address const utxos = await client.xChain.getUTXOs({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], sourceChain: "P", // Optional: specify source chain limit: 100, }); console.log("Number of UTXOs:", utxos.utxos.length); // Paginate through UTXOs if needed if (utxos.endIndex) { const moreUtxos = await client.xChain.getUTXOs({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], startIndex: utxos.endIndex, limit: 100, }); } ``` ### Query Transaction Information ```typescript // Get transaction const tx = await client.xChain.getTx({ txID: "11111111111111111111111111111111LpoYY", encoding: "hex", }); // Get transaction status const status = await client.xChain.getTxStatus({ txID: "11111111111111111111111111111111LpoYY", }); console.log("Transaction status:", status.status); // Get transaction fees const txFee = await client.xChain.getTxFee(); console.log("Transaction fee:", txFee.txFee); console.log("Create asset fee:", txFee.createAssetTxFee); ``` ### Query Block Information ```typescript // Get current height const height = await client.xChain.getHeight(); console.log("Current X-Chain height:", height); // Get block by height const block = await client.xChain.getBlockByHeight({ height: Number(height), encoding: "hex", }); // Get block by ID const blockById = await client.xChain.getBlock({ blockID: "d7WYmb8VeZNHsny3EJCwMm6QA37s1EHwMxw1Y71V3FqPZ5EFG", encoding: "hex", }); ``` ## Wallet Operations For transaction operations (preparing and sending transactions), use the wallet client: ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Prepare and send base transaction const baseTxn = await walletClient.xChain.prepareBaseTxn({ outputs: [ { addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], amount: 1, // 1 AVAX }, ], }); const txID = await walletClient.sendXPTransaction(baseTxn); console.log("Transaction sent:", txID); ``` For complete wallet operations documentation, see [X-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/x-chain-wallet). ## Next Steps - **[X-Chain Methods Reference](/avalanche-sdk/client-sdk/methods/public-methods/x-chain)** - Complete method documentation - **[X-Chain Wallet Methods](/avalanche-sdk/client-sdk/methods/wallet-methods/x-chain-wallet)** - Transaction preparation and signing - **[Wallet Client](/avalanche-sdk/client-sdk/clients/wallet-client)** - Complete wallet operations - **[P-Chain Client](/avalanche-sdk/client-sdk/clients/p-chain-client)** - Validator and staking operations - **[C-Chain Client](/avalanche-sdk/client-sdk/clients/c-chain-client)** - EVM operations # Utilities (/docs/tooling/avalanche-sdk/client/utils) --- title: "Utilities" icon: "tools" --- ## Overview The Avalanche SDK provides utility functions for AVAX unit conversion, CB58 encoding/decoding, transaction serialization, and UTXO operations. All viem utilities are also re-exported for EVM operations. **Note:** All utility functions are synchronous unless marked as `async`. Handle errors appropriately when working with blockchain data. ## Importing Utilities Import utilities from `@avalanche-sdk/client/utils`: ```typescript import { // AVAX conversions avaxToNanoAvax, nanoAvaxToAvax, avaxToWei, weiToAvax, weiToNanoAvax, nanoAvaxToWei, // CB58 encoding CB58ToHex, hexToCB58, // Transaction serialization getTxFromBytes, getUnsignedTxFromBytes, // UTXO operations getUtxoFromBytes, getUtxosForAddress, buildUtxoBytes, } from "@avalanche-sdk/client/utils"; // Viem utilities are also available import { hexToBytes, bytesToHex, isAddress } from "@avalanche-sdk/client/utils"; ``` ## AVAX Unit Conversion Avalanche uses different units for different chains: - **AVAX**: Human-readable unit (1 AVAX) - **nanoAVAX (nAVAX)**: Smallest unit on P-Chain and X-Chain and C-Chain atomics (1 AVAX = 10^9 nAVAX) - **wei**: Used on C-Chain (1 AVAX = 10^18 wei) ### avaxToNanoAvax Converts AVAX to nanoAVAX for P-Chain/X-Chain or C-Chain Atomic operations. **Function Signature:** ```typescript function avaxToNanoAvax(amount: number): bigint; ``` **Parameters:** | Name | Type | Required | Description | | -------- | -------- | -------- | -------------- | | `amount` | `number` | Yes | Amount in AVAX | **Returns:** | Type | Description | | -------- | ------------------ | | `bigint` | Amount in nanoAVAX | **Example:** ```typescript import { avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const nanoAvax = avaxToNanoAvax(1.5); console.log(nanoAvax); // 1500000000n // Use in P-Chain transaction const tx = await walletClient.pChain.prepareBaseTxn({ outputs: [ { addresses: ["P-avax1..."], amount: Number(nanoAvax), }, ], }); ``` ### nanoAvaxToAvax Converts nanoAVAX back to AVAX for display purposes. **Function Signature:** ```typescript function nanoAvaxToAvax(amount: bigint): number; ``` **Parameters:** | Name | Type | Required | Description | | -------- | -------- | -------- | ------------------ | | `amount` | `bigint` | Yes | Amount in nanoAVAX | **Returns:** | Type | Description | | -------- | -------------- | | `number` | Amount in AVAX | **Example:** ```typescript import { nanoAvaxToAvax } from "@avalanche-sdk/client/utils"; const balance = await walletClient.pChain.getBalance({ addresses: ["P-avax1..."], }); const avax = nanoAvaxToAvax(BigInt(balance.balance || 0)); console.log(`Balance: ${avax} AVAX`); ``` ### avaxToWei Converts AVAX to wei for C-Chain operations. **Function Signature:** ```typescript function avaxToWei(amount: number): bigint; ``` **Parameters:** | Name | Type | Required | Description | | -------- | -------- | -------- | -------------- | | `amount` | `number` | Yes | Amount in AVAX | **Returns:** | Type | Description | | -------- | ------------- | | `bigint` | Amount in wei | **Example:** ```typescript import { avaxToWei } from "@avalanche-sdk/client/utils"; const wei = avaxToWei(1.5); console.log(wei); // 1500000000000000000n // Use in C-Chain transaction const txHash = await walletClient.cChain.sendTransaction({ to: "0x...", value: wei, }); ``` ### weiToAvax Converts wei back to AVAX for display. **Function Signature:** ```typescript function weiToAvax(amount: bigint): bigint; ``` **Parameters:** | Name | Type | Required | Description | | -------- | -------- | -------- | ------------- | | `amount` | `bigint` | Yes | Amount in wei | **Returns:** | Type | Description | | -------- | -------------------------- | | `bigint` | Amount in AVAX (as bigint) | **Example:** ```typescript import { weiToAvax } from "@avalanche-sdk/client/utils"; const balance = await walletClient.cChain.getBalance({ address: "0x...", }); const avax = weiToAvax(balance); console.log(`Balance: ${avax} AVAX`); ``` ### weiToNanoAvax Converts wei to nanoAVAX for cross-chain operations. **Function Signature:** ```typescript function weiToNanoAvax(amount: bigint): bigint; ``` **Parameters:** | Name | Type | Required | Description | | -------- | -------- | -------- | ------------- | | `amount` | `bigint` | Yes | Amount in wei | **Returns:** | Type | Description | | -------- | ------------------ | | `bigint` | Amount in nanoAVAX | **Example:** ```typescript import { weiToNanoAvax } from "@avalanche-sdk/client/utils"; const cChainBalance = await walletClient.cChain.getBalance({ address: "0x...", }); // Convert to nanoAVAX for P-Chain transfer const nanoAvax = weiToNanoAvax(cChainBalance); ``` ### nanoAvaxToWei Converts nanoAVAX to wei for cross-chain operations. **Function Signature:** ```typescript function nanoAvaxToWei(amount: bigint): bigint; ``` **Parameters:** | Name | Type | Required | Description | | -------- | -------- | -------- | ------------------ | | `amount` | `bigint` | Yes | Amount in nanoAVAX | **Returns:** | Type | Description | | -------- | ------------- | | `bigint` | Amount in wei | **Example:** ```typescript import { nanoAvaxToWei } from "@avalanche-sdk/client/utils"; const pChainBalance = await walletClient.pChain.getBalance({ addresses: ["P-avax1..."], }); // Convert to wei for C-Chain transfer const wei = nanoAvaxToWei(BigInt(pChainBalance.balance || 0)); ``` ## CB58 Encoding/Decoding CB58 is Avalanche's base58 encoding format used for transaction IDs, asset IDs, and addresses. ### CB58ToHex Converts CB58-encoded strings to hexadecimal format. **Function Signature:** ```typescript function CB58ToHex(cb58: string): Hex; ``` **Parameters:** | Name | Type | Required | Description | | ------ | -------- | -------- | ------------------- | | `cb58` | `string` | Yes | CB58 encoded string | **Returns:** | Type | Description | | ----- | ----------------------------------- | | `Hex` | Hexadecimal string with `0x` prefix | **Example:** ```typescript import { CB58ToHex } from "@avalanche-sdk/client/utils"; const txId = "mYxFK3CWs6iMFFaRx4wmVLDUtnktzm2o9Mhg9AG6JSzRijy5V"; const hex = CB58ToHex(txId); console.log(hex); // 0x... // Use with hex-based APIs const tx = await client.pChain.getAtomicTx({ txID: hex }); ``` ### hexToCB58 Converts hexadecimal strings to CB58 format. **Function Signature:** ```typescript function hexToCB58(hex: Hex): string; ``` **Parameters:** | Name | Type | Required | Description | | ----- | ----- | -------- | ----------------------------------- | | `hex` | `Hex` | Yes | Hexadecimal string with `0x` prefix | **Returns:** | Type | Description | | -------- | ------------------- | | `string` | CB58 encoded string | **Example:** ```typescript import { hexToCB58 } from "@avalanche-sdk/client/utils"; const hex = "0x1234567890abcdef"; const cb58 = hexToCB58(hex); console.log(cb58); // CB58 encoded string ``` ## Transaction Serialization ### getTxFromBytes Parses signed transaction bytes to extract the transaction and credentials. **Function Signature:** ```typescript function getTxFromBytes( txBytes: string, chainAlias: "P" | "X" | "C" ): [Common.Transaction, Credential[]]; ``` **Parameters:** | Name | Type | Required | Description | | ------------ | ------------------- | -------- | ------------------------------- | | `txBytes` | `string` | Yes | Transaction bytes as hex string | | `chainAlias` | `"P" \| "X" \| "C"` | Yes | Chain alias | **Returns:** | Type | Description | | ------------------------------------ | -------------------------------------------------- | | `[Common.Transaction, Credential[]]` | Tuple containing transaction and credentials array | **Example:** ```typescript import { getTxFromBytes } from "@avalanche-sdk/client/utils"; const txHex = "0x1234567890abcdef..."; const [tx, credentials] = getTxFromBytes(txHex, "P"); console.log("Transaction ID:", tx.getId().toString()); console.log("Signatures:", credentials.length); ``` ### getUnsignedTxFromBytes Parses unsigned transaction bytes to get an unsigned transaction object. **Function Signature:** ```typescript function getUnsignedTxFromBytes( txBytes: string, chainAlias: "P" | "X" | "C" ): UnsignedTx; ``` **Parameters:** | Name | Type | Required | Description | | ------------ | ------------------- | -------- | ------------------------------- | | `txBytes` | `string` | Yes | Transaction bytes as hex string | | `chainAlias` | `"P" \| "X" \| "C"` | Yes | Chain alias | **Returns:** | Type | Description | | ------------ | ---------------------------------- | | `UnsignedTx` | Parsed unsigned transaction object | **Example:** ```typescript import { getUnsignedTxFromBytes } from "@avalanche-sdk/client/utils"; const txHex = "0x1234567890abcdef..."; const unsignedTx = getUnsignedTxFromBytes(txHex, "P"); console.log("Transaction ID:", unsignedTx.txID); console.log("Transaction bytes:", unsignedTx.toBytes()); ``` ## UTXO Operations ### getUtxoFromBytes Parses UTXO bytes to get a UTXO object. **Function Signature:** ```typescript function getUtxoFromBytes( utxoBytesOrHex: string | Uint8Array, chainAlias: "P" | "X" | "C" ): Utxo; ``` **Parameters:** | Name | Type | Required | Description | | ---------------- | ---------------------- | -------- | -------------------------------------- | | `utxoBytesOrHex` | `string \| Uint8Array` | Yes | UTXO bytes as hex string or Uint8Array | | `chainAlias` | `"P" \| "X" \| "C"` | Yes | Chain alias | **Returns:** | Type | Description | | ------ | ------------------ | | `Utxo` | Parsed UTXO object | **Example:** ```typescript import { getUtxoFromBytes } from "@avalanche-sdk/client/utils"; const utxoHex = "0x1234567890abcdef..."; const utxo = getUtxoFromBytes(utxoHex, "P"); console.log("UTXO ID:", utxo.utxoID); console.log("Asset ID:", utxo.assetID); console.log("Output:", utxo.output); ``` ### getUtxosForAddress Fetches all UTXOs for a given address on a specific chain. This function handles pagination automatically. **Function Signature:** ```typescript function getUtxosForAddress( client: AvalancheWalletCoreClient, params: { address: string; chainAlias: "P" | "X" | "C"; sourceChain?: string; } ): Promise; ``` **Parameters:** | Name | Type | Required | Description | | -------- | --------------------------- | -------- | -------------------------- | | `client` | `AvalancheWalletCoreClient` | Yes | The wallet client instance | | `params` | `object` | Yes | Parameters object | **params object:** | Name | Type | Required | Description | | ------------- | ------------------- | -------- | --------------------------------------- | | `address` | `string` | Yes | Address to query | | `chainAlias` | `"P" \| "X" \| "C"` | Yes | Chain alias | | `sourceChain` | `string` | No | Source chain ID for import transactions | **Returns:** | Type | Description | | ----------------- | --------------------- | | `Promise` | Array of UTXO objects | **Example:** ```typescript import { getUtxosForAddress } from "@avalanche-sdk/client/utils"; import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const walletClient = createAvalancheWalletClient({ account: myAccount, chain: avalanche, transport: { type: "http" }, }); const utxos = await getUtxosForAddress(walletClient, { address: "P-avax1...", chainAlias: "P", }); console.log(`Found ${utxos.length} UTXOs`); ``` ### buildUtxoBytes Builds UTXO bytes from parameters. Useful for reconstructing UTXOs or creating test data. **Function Signature:** ```typescript function buildUtxoBytes( txHash: string, outputIndex: number, assetId: string, amount: string, addresses: string[], locktime: string, threshold: number ): `0x${string}`; ``` **Parameters:** | Name | Type | Required | Description | | ------------- | ---------- | -------- | ------------------------------------------- | | `txHash` | `string` | Yes | Transaction hash in CB58 format | | `outputIndex` | `number` | Yes | Output index in the transaction | | `assetId` | `string` | Yes | Asset ID in CB58 format | | `amount` | `string` | Yes | Amount as string | | `addresses` | `string[]` | Yes | Array of addresses that can spend this UTXO | | `locktime` | `string` | Yes | UNIX timestamp locktime in seconds | | `threshold` | `number` | Yes | Signature threshold | **Returns:** | Type | Description | | ------------------- | ------------------------ | | `` `0x${string}` `` | UTXO bytes as hex string | **Example:** ```typescript import { buildUtxoBytes } from "@avalanche-sdk/client/utils"; const utxoBytes = buildUtxoBytes( "mYxFK3CWs6iMFFaRx4wmVLDUtnktzm2o9Mhg9AG6JSzRijy5V", 0, "U8iRqJoiJm8xZHAacmvYyZVwqQx6uDNtQeP3CQ6fcgQk3JqnK", "111947", ["P-fuji1nv6w7m6egkwhkcvz96ze3qmzyk5gt6csqz7ejq"], "0", 1 ); console.log("UTXO bytes:", utxoBytes); ``` ## Viem Utilities The SDK re-exports all utilities from viem for EVM operations. See the [viem utilities documentation](https://viem.sh/docs/utilities) for complete reference. **Common Categories:** - **Encoding/Decoding**: `bytesToHex`, `hexToBytes`, `stringToHex` - **ABI Operations**: `encodeAbiParameters`, `decodeAbiParameters`, `parseAbiItem` - **Address Operations**: `getAddress`, `isAddress`, `checksumAddress` - **Number Operations**: `bytesToBigInt`, `hexToNumber`, `numberToHex` - **Hash Operations**: `keccak256`, `sha256`, `ripemd160` - **Signature Operations**: `recoverAddress`, `verifyMessage` ## Common Patterns ### Converting Between Units ```typescript import { avaxToNanoAvax, nanoAvaxToAvax, avaxToWei, weiToAvax, } from "@avalanche-sdk/client/utils"; // P-Chain: AVAX → nanoAVAX const nanoAvax = avaxToNanoAvax(1.5); // C-Chain: AVAX → wei const wei = avaxToWei(1.5); // Display: nanoAVAX → AVAX const avax = nanoAvaxToAvax(nanoAvax); ``` ### Working with Transaction IDs ```typescript import { CB58ToHex, hexToCB58 } from "@avalanche-sdk/client/utils"; // Convert CB58 to hex for API calls const txId = "mYxFK3CWs6iMFFaRx4wmVLDUtnktzm2o9Mhg9AG6JSzRijy5V"; const hex = CB58ToHex(txId); // Convert hex back to CB58 for display const cb58 = hexToCB58(hex); ``` ### Parsing Transactions ```typescript import { getTxFromBytes } from "@avalanche-sdk/client/utils"; const txHex = "0x..."; const [tx, credentials] = getTxFromBytes(txHex, "P"); // Access transaction details const txId = tx.getId().toString(); const numSignatures = credentials.length; ``` ## Next Steps - **[Account Management](accounts)** - Working with accounts - **[Transaction Signing](methods/wallet-methods/wallet)** - Signing and sending transactions - **[Chain Clients](clients)** - Chain-specific operations - **[Viem Documentation](https://viem.sh/docs/utilities)** - Complete viem utilities reference # Chain Configuration (/docs/tooling/avalanche-sdk/interchain/chains) --- title: Chain Configuration icon: link --- ## Overview Chain configurations define the blockchain networks for interchain operations. Each chain includes network details and interchain contract addresses. ## Available Chains ### avalancheFuji Avalanche Fuji testnet configuration. ```typescript import { avalancheFuji } from "@avalanche-sdk/interchain/chains"; ``` ### dispatch Dispatch subnet configuration. ```typescript import { dispatch } from "@avalanche-sdk/interchain/chains"; ``` ## ChainConfig Type ```typescript interface ChainConfig { id: number; name: string; network: string; nativeCurrency: { name: string; symbol: string; decimals: number; }; rpcUrls: { default: { http: string[]; }; }; blockchainId: string; interchainContracts: { teleporterRegistry: Address; teleporterManager: Address; }; } ``` ## Using Chains ```typescript import { createICMClient } from "@avalanche-sdk/interchain"; import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains"; const icm = createICMClient(wallet, avalancheFuji, dispatch); // Or specify per call await icm.sendMsg({ sourceChain: avalancheFuji, destinationChain: dispatch, message: "Hello!", }); ``` ## Custom Chains You can define custom chains for interchain operations by extending the base chain configuration with interchain-specific properties. Custom chains are useful when working with custom subnets or L1 chains that support Teleporter. ### Defining a Custom Chain Use `defineChain` from `@avalanche-sdk/client` to create a chain configuration, then cast it to `ChainConfig` to add interchain contract addresses: ```typescript import { defineChain } from "@avalanche-sdk/client"; import type { ChainConfig } from "@avalanche-sdk/interchain/chains"; export const myCustomChain = defineChain({ id: 12345, // Your chain ID name: "My Custom Chain", network: "my-custom-chain", nativeCurrency: { decimals: 18, name: "Token", symbol: "TKN", }, rpcUrls: { default: { http: ["https://api.example.com/ext/bc/C/rpc"], }, }, blockExplorers: { default: { name: "Explorer", url: "https://explorer.example.com", }, }, // Interchain-specific properties blockchainId: "0x...", // Your blockchain ID (hex-encoded) interchainContracts: { teleporterRegistry: "0x...", // Teleporter registry contract address teleporterManager: "0x...", // Teleporter manager contract address }, }) as ChainConfig; ``` ### Required Properties | Property | Type | Description | | --------------------- | -------- | ------------------------------------------------ | | `id` | `number` | Unique chain identifier (EVM chain ID) | | `name` | `string` | Human-readable chain name | | `network` | `string` | Network identifier (used for wallet connections) | | `nativeCurrency` | `object` | Native token configuration | | `rpcUrls` | `object` | RPC endpoint URLs | | `blockchainId` | `string` | Avalanche blockchain ID (hex-encoded) | | `interchainContracts` | `object` | Teleporter contract addresses | ### Interchain Contracts The `interchainContracts` object must include: - **`teleporterRegistry`**: Address of the Teleporter registry contract on this chain - **`teleporterManager`**: Address of the Teleporter manager contract on this chain These contracts enable cross-chain messaging and token transfers. Ensure they are deployed and configured on your chain before using interchain operations. ### Example: Custom Subnet Chain ```typescript import { defineChain } from "@avalanche-sdk/client"; import type { ChainConfig } from "@avalanche-sdk/interchain/chains"; export const mySubnet = defineChain({ id: 54321, name: "My Subnet", network: "my-subnet", nativeCurrency: { decimals: 18, name: "Avalanche", symbol: "AVAX", }, rpcUrls: { default: { http: ["https://subnets.avax.network/mysubnet/mainnet/rpc"], }, }, blockExplorers: { default: { name: "Subnet Explorer", url: "https://subnets.avax.network/mysubnet", }, }, blockchainId: "0x1234567890abcdef1234567890abcdef12345678", interchainContracts: { teleporterRegistry: "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228", teleporterManager: "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf", }, }) as ChainConfig; ``` ### Using Custom Chains Once defined, use your custom chain with ICM and ICTT clients: ```typescript import { createICMClient } from "@avalanche-sdk/interchain"; import { myCustomChain } from "./chains/myCustomChain"; import { avalancheFuji } from "@avalanche-sdk/interchain/chains"; const icm = createICMClient(wallet, avalancheFuji, myCustomChain); // Send message to custom chain await icm.sendMsg({ sourceChain: avalancheFuji, destinationChain: myCustomChain, message: "Hello from Fuji to My Custom Chain!", }); ``` ### Tips - **Blockchain ID**: Use the hex-encoded blockchain ID from your chain's configuration. You can find this in your chain's genesis data or by querying the chain's info API. - **Contract Addresses**: Ensure Teleporter contracts are deployed on your chain before using interchain operations. Contact your chain operator or refer to your chain's documentation for the correct addresses. - **RPC URLs**: Provide reliable RPC endpoints. Consider using multiple endpoints for redundancy. - **Testnet vs Mainnet**: Use the `testnet` property to mark testnet chains, which helps with wallet integrations and explorer links. For more information on chain configuration, see the [Viem chains documentation](https://viem.sh/docs/chains/introduction). # Interchain Messaging (/docs/tooling/avalanche-sdk/interchain/icm) --- title: Interchain Messaging icon: message-square --- ## Overview Send arbitrary messages between Avalanche chains and subnets using the Teleporter protocol. Messages are encoded as strings and delivered cross-chain. ## Create Client ```typescript import { createICMClient } from "@avalanche-sdk/interchain"; import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains"; // Setup wallet const account = privateKeyToAvalancheAccount("0x..."); const wallet = createAvalancheWalletClient({ account, chain: avalancheFuji, transport: { type: "http" }, }); const icm = createICMClient(wallet); // Or with default chains const icm = createICMClient(wallet, sourceChain, destinationChain); ``` The wallet client's chain configuration must match the `sourceChain` used in your interchain operations. For example, if you're sending messages from Fuji testnet, ensure your wallet client is configured with the Fuji chain. Mismatched chains will result in an "invalid sender" error. ## Methods Send a cross-chain message # Methods (/docs/tooling/avalanche-sdk/interchain/icm/methods) --- title: Methods icon: code --- ## sendMsg Sends a cross-chain message to the specified destination chain. ### Parameters | Parameter | Type | Required | Description | | ------------------------- | --------------------------------------------- | -------- | -------------------------------------------- | | `message` | `string` | Yes | Message content to send | | `sourceChain` | `ChainConfig` | Yes\* | Source chain configuration | | `destinationChain` | `ChainConfig` | Yes\* | Destination chain configuration | | `recipientAddress` | `0x${string}` | No | Recipient address (defaults to zero address) | | `feeInfo` | `{ feeTokenAddress: string, amount: bigint }` | No | Fee token and amount | | `requiredGasLimit` | `bigint` | No | Gas limit for execution (default: 100000) | | `allowedRelayerAddresses` | `string[]` | No | Allowed relayer addresses | \* Required if not set in client constructor ### Returns | Type | Description | | ----------------- | ---------------- | | `Promise` | Transaction hash | ### Example ```typescript import { createICMClient } from "@avalanche-sdk/interchain"; import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains"; const icm = createICMClient(wallet); // Simple message const hash = await icm.sendMsg({ sourceChain: avalancheFuji, destinationChain: dispatch, message: "Hello from Avalanche!", }); // With options const hash = await icm.sendMsg({ sourceChain: avalancheFuji, destinationChain: dispatch, message: "Hello from Avalanche!", recipientAddress: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", feeInfo: { feeTokenAddress: "0x0000000000000000000000000000000000000000", amount: 0n, }, requiredGasLimit: 200000n, }); ``` # Building Warp Messages (/docs/tooling/avalanche-sdk/interchain/warp/building) --- title: Building Warp Messages icon: hammer --- ## Building Messages Build unsigned Warp messages for signing and broadcasting. ## RegisterL1ValidatorMessage ### Methods | Method | Parameters | Returns | Description | | ------------ | ------------------------------------------------------ | ---------------------------- | ----------------- | | `fromValues` | `nodeID: string, publicKey: string, signature: string` | `RegisterL1ValidatorMessage` | Build from values | | `toHex` | - | `string` | Convert to hex | ### Example ```typescript import { RegisterL1ValidatorMessage } from "@avalanche-sdk/interchain/warp"; const msg = RegisterL1ValidatorMessage.fromValues( "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", "0x...", "0x..." ); const hex = msg.toHex(); ``` ## L1ValidatorWeightMessage ### Methods | Method | Parameters | Returns | Description | | ------------ | --------------------------------------------------- | -------------------------- | ----------------- | | `fromValues` | `nodeID: string, weight: bigint, startTime: bigint` | `L1ValidatorWeightMessage` | Build from values | | `toHex` | - | `string` | Convert to hex | ### Example ```typescript import { L1ValidatorWeightMessage } from "@avalanche-sdk/interchain/warp"; const msg = L1ValidatorWeightMessage.fromValues( "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", 4n, 41n ); const hex = msg.toHex(); ``` ## AddressedCall Build AddressedCall payload from a message. ### Methods | Method | Parameters | Returns | Description | | ------------ | ----------------------------------------- | --------------- | ----------------- | | `fromValues` | `sourceAddress: Address, payload: string` | `AddressedCall` | Build from values | | `toHex` | - | `string` | Convert to hex | ### Example ```typescript import { AddressedCall, L1ValidatorWeightMessage, } from "@avalanche-sdk/interchain/warp"; const msg = L1ValidatorWeightMessage.fromValues( "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", 4n, 41n ); const addressedCall = AddressedCall.fromValues( "0x35F884853114D298D7aA8607f4e7e0DB52205f07", msg.toHex() ); ``` ## WarpUnsignedMessage Build unsigned Warp message from AddressedCall. ### Methods | Method | Parameters | Returns | Description | | ------------ | ------------------------------------------------------------------------ | --------------------- | ----------------- | | `fromValues` | `networkID: number, sourceChainID: string, addressedCallPayload: string` | `WarpUnsignedMessage` | Build from values | | `toHex` | - | `string` | Convert to hex | ### Example ```typescript import { AddressedCall, L1ValidatorWeightMessage, WarpUnsignedMessage, } from "@avalanche-sdk/interchain/warp"; // Build message const msg = L1ValidatorWeightMessage.fromValues( "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", 4n, 41n ); // Build AddressedCall const addressedCall = AddressedCall.fromValues( "0x35F884853114D298D7aA8607f4e7e0DB52205f07", msg.toHex() ); // Build unsigned Warp message const warpUnsignedMsg = WarpUnsignedMessage.fromValues( 1, "251q44yFiimeVSHaQbBk69TzoeYqKu9VagGtLVqo92LphUxjmR", addressedCall.toHex() ); const hex = warpUnsignedMsg.toHex(); ``` # Warp Messages (/docs/tooling/avalanche-sdk/interchain/warp) --- title: Warp Messages icon: layers --- ## Overview Warp messages enable cross-chain communication in Avalanche. Parse and build Warp protocol messages for validator registration, weight updates, and subnet conversions. ## Supported Message Types - **RegisterL1ValidatorMessage** - Register L1 validators - **L1ValidatorWeightMessage** - Update validator weights - **L1ValidatorRegistrationMessage** - L1 validator registration - **SubnetToL1ConversionMessage** - Subnet to L1 conversion ## Quick Start ```typescript import { WarpMessage, RegisterL1ValidatorMessage, } from "@avalanche-sdk/interchain/warp"; // Parse a signed Warp message const signedWarpMsg = WarpMessage.fromHex(signedWarpMsgHex); // Parse specific message type const registerMsg = RegisterL1ValidatorMessage.fromHex(signedWarpMsgHex); ``` ## Methods Parse Warp messages Build Warp messages # Parsing Warp Messages (/docs/tooling/avalanche-sdk/interchain/warp/parsing) --- title: Parsing Warp Messages icon: search --- ## WarpMessage Parse a signed Warp message from hex. ### Methods | Method | Parameters | Returns | Description | | --------- | ------------- | ------------- | ------------------------- | | `fromHex` | `hex: string` | `WarpMessage` | Parse signed Warp message | ### Example ```typescript import { WarpMessage } from "@avalanche-sdk/interchain/warp"; const signedWarpMsgHex = "0x..."; const warpMsg = WarpMessage.fromHex(signedWarpMsgHex); // Access message properties console.log(warpMsg.networkID); console.log(warpMsg.sourceChainID); console.log(warpMsg.addressedCallPayload); console.log(warpMsg.signatures); ``` ## RegisterL1ValidatorMessage Parse a Register L1 Validator message. ### Methods | Method | Parameters | Returns | Description | | --------- | ------------- | ---------------------------- | -------------- | | `fromHex` | `hex: string` | `RegisterL1ValidatorMessage` | Parse from hex | ### Example ```typescript import { RegisterL1ValidatorMessage } from "@avalanche-sdk/interchain/warp"; const msg = RegisterL1ValidatorMessage.fromHex(signedWarpMsgHex); console.log(msg.nodeID); console.log(msg.publicKey); console.log(msg.signature); ``` ## L1ValidatorWeightMessage Parse an L1 Validator Weight message. ### Methods | Method | Parameters | Returns | Description | | --------- | ------------- | -------------------------- | -------------- | | `fromHex` | `hex: string` | `L1ValidatorWeightMessage` | Parse from hex | ### Example ```typescript import { L1ValidatorWeightMessage } from "@avalanche-sdk/interchain/warp"; const msg = L1ValidatorWeightMessage.fromHex(signedWarpMsgHex); console.log(msg.nodeID); console.log(msg.weight); console.log(msg.startTime); ``` ## L1ValidatorRegistrationMessage Parse an L1 Validator Registration message. ### Methods | Method | Parameters | Returns | Description | | --------- | ------------- | -------------------------------- | -------------- | | `fromHex` | `hex: string` | `L1ValidatorRegistrationMessage` | Parse from hex | ## SubnetToL1ConversionMessage Parse a Subnet to L1 Conversion message. ### Methods | Method | Parameters | Returns | Description | | --------- | ------------- | ----------------------------- | -------------- | | `fromHex` | `hex: string` | `SubnetToL1ConversionMessage` | Parse from hex | ### Example ```typescript import { SubnetToL1ConversionMessage } from "@avalanche-sdk/interchain/warp"; const msg = SubnetToL1ConversionMessage.fromHex(signedWarpMsgHex); console.log(msg.subnetID); console.log(msg.assetID); console.log(msg.initialSupply); ``` # Deployment Methods (/docs/tooling/avalanche-sdk/interchain/ictt/deployment) --- title: Deployment Methods icon: package --- ## deployERC20Token Deploys a new ERC20 token on the source chain. ### Parameters | Parameter | Type | Required | Description | | --------------- | -------------- | -------- | -------------------------------------------------------- | | `walletClient` | `WalletClient` | Yes | Wallet client for signing | | `sourceChain` | `ChainConfig` | Yes\* | Source chain configuration | | `name` | `string` | Yes | Token name | | `symbol` | `string` | Yes | Token symbol | | `initialSupply` | `number` | Yes | Initial token supply | | `recipient` | `Address` | No | Recipient of initial supply (defaults to wallet address) | \* Required if not set in client constructor ### Returns | Type | Description | | ---------------------------------------------------------------- | ------------------------------------- | | `Promise<{ txHash: 0x${string}, contractAddress: 0x${string} }>` | Transaction hash and contract address | ### Example ```typescript const { txHash, contractAddress } = await ictt.deployERC20Token({ walletClient: wallet, sourceChain: avalancheFuji, name: "My Token", symbol: "MTK", initialSupply: 1000000, }); ``` ## deployTokenHomeContract Deploys a token home contract on the source chain. ### Parameters | Parameter | Type | Required | Description | | -------------------------- | -------------- | -------- | -------------------------- | | `walletClient` | `WalletClient` | Yes | Wallet client for signing | | `sourceChain` | `ChainConfig` | Yes\* | Source chain configuration | | `erc20TokenAddress` | `Address` | Yes | ERC20 token address | | `minimumTeleporterVersion` | `number` | Yes | Minimum Teleporter version | | `tokenHomeCustomByteCode` | `string` | No | Custom bytecode | | `tokenHomeCustomABI` | `ABI` | No | Custom ABI | ### Returns | Type | Description | | ---------------------------------------------------------------- | ------------------------------------- | | `Promise<{ txHash: 0x${string}, contractAddress: 0x${string} }>` | Transaction hash and contract address | ### Example ```typescript const { txHash, contractAddress } = await ictt.deployTokenHomeContract({ walletClient: wallet, sourceChain: avalancheFuji, erc20TokenAddress: tokenAddress, minimumTeleporterVersion: 1, }); ``` ## deployTokenRemoteContract Deploys a token remote contract on the destination chain. ### Parameters | Parameter | Type | Required | Description | | --------------------------- | -------------- | -------- | ------------------------------- | | `walletClient` | `WalletClient` | Yes | Wallet client for signing | | `sourceChain` | `ChainConfig` | Yes\* | Source chain configuration | | `destinationChain` | `ChainConfig` | Yes\* | Destination chain configuration | | `tokenHomeContract` | `Address` | Yes | Token home contract address | | `tokenRemoteCustomByteCode` | `string` | No | Custom bytecode | | `tokenRemoteCustomABI` | `ABI` | No | Custom ABI | ### Returns | Type | Description | | ---------------------------------------------------------------- | ------------------------------------- | | `Promise<{ txHash: 0x${string}, contractAddress: 0x${string} }>` | Transaction hash and contract address | ### Example ```typescript const { txHash, contractAddress } = await ictt.deployTokenRemoteContract({ walletClient: wallet, sourceChain: avalancheFuji, destinationChain: dispatch, tokenHomeContract: tokenHomeAddress, }); ``` ## registerRemoteWithHome Registers the token remote contract with the token home contract. ### Parameters | Parameter | Type | Required | Description | | --------------------- | -------------- | -------- | ------------------------------- | | `walletClient` | `WalletClient` | Yes | Wallet client for signing | | `sourceChain` | `ChainConfig` | Yes\* | Source chain configuration | | `destinationChain` | `ChainConfig` | Yes\* | Destination chain configuration | | `tokenRemoteContract` | `Address` | Yes | Token remote contract address | | `feeTokenAddress` | `Address` | No | Fee token address | | `feeAmount` | `number` | No | Fee amount | ### Returns | Type | Description | | ---------------------------------- | ---------------- | | `Promise<{ txHash: 0x${string} }>` | Transaction hash | ### Example ```typescript const { txHash } = await ictt.registerRemoteWithHome({ walletClient: wallet, sourceChain: avalancheFuji, destinationChain: dispatch, tokenRemoteContract: tokenRemoteAddress, }); ``` # Interchain Token Transfers (/docs/tooling/avalanche-sdk/interchain/ictt) --- title: Interchain Token Transfers icon: coins --- ## Overview Transfer ERC20 tokens between Avalanche chains using the Teleporter protocol. Requires deploying token home and remote contracts for each token pair. ## Create Client ```typescript import { createICTTClient } from "@avalanche-sdk/interchain"; const ictt = createICTTClient(); // Or with default chains const ictt = createICTTClient(sourceChain, destinationChain); ``` ## Workflow 1. **Deploy ERC20 Token** - Deploy your token on the source chain 2. **Deploy Token Home** - Deploy home contract on source chain 3. **Deploy Token Remote** - Deploy remote contract on destination chain 4. **Register Remote** - Register remote with home contract 5. **Approve Token** - Approve home contract to spend tokens 6. **Send Tokens** - Transfer tokens cross-chain The wallet client's chain configuration must match the `sourceChain` used in your interchain operations. For example, if you're sending messages from Fuji testnet, ensure your wallet client is configured with the Fuji chain. Mismatched chains will result in an "invalid sender" error. ## Methods Deploy tokens and contracts Transfer tokens cross-chain # Transfer Methods (/docs/tooling/avalanche-sdk/interchain/ictt/transfers) --- title: Transfer Methods icon: arrow-right --- ## approveToken Approves the token home contract to spend tokens on the source chain. ### Parameters | Parameter | Type | Required | Description | | ------------------- | -------------- | -------- | --------------------------------- | | `walletClient` | `WalletClient` | Yes | Wallet client for signing | | `sourceChain` | `ChainConfig` | Yes\* | Source chain configuration | | `tokenHomeContract` | `Address` | Yes | Token home contract address | | `tokenAddress` | `Address` | Yes | ERC20 token address | | `amountInBaseUnit` | `number` | Yes | Amount to approve (in base units) | \* Required if not set in client constructor ### Returns | Type | Description | | ---------------------------------- | ---------------- | | `Promise<{ txHash: 0x${string} }>` | Transaction hash | ### Example ```typescript const { txHash } = await ictt.approveToken({ walletClient: wallet, sourceChain: avalancheFuji, tokenHomeContract: tokenHomeAddress, tokenAddress: tokenAddress, amountInBaseUnit: 1000, }); ``` ## sendToken Sends tokens from the source chain to the destination chain. ### Parameters | Parameter | Type | Required | Description | | --------------------- | -------------- | -------- | -------------------------------------- | | `walletClient` | `WalletClient` | Yes | Wallet client for signing | | `sourceChain` | `ChainConfig` | Yes\* | Source chain configuration | | `destinationChain` | `ChainConfig` | Yes\* | Destination chain configuration | | `tokenHomeContract` | `Address` | Yes | Token home contract address | | `tokenRemoteContract` | `Address` | Yes | Token remote contract address | | `recipient` | `Address` | Yes | Recipient address on destination chain | | `amountInBaseUnit` | `number` | Yes | Amount to send (in base units) | | `feeTokenAddress` | `Address` | No | Fee token address | | `feeAmount` | `number` | No | Fee amount | \* Required if not set in client constructor ### Returns | Type | Description | | ---------------------------------- | ---------------- | | `Promise<{ txHash: 0x${string} }>` | Transaction hash | ### Example ```typescript const { txHash } = await ictt.sendToken({ walletClient: wallet, sourceChain: avalancheFuji, destinationChain: dispatch, tokenHomeContract: tokenHomeAddress, tokenRemoteContract: tokenRemoteAddress, recipient: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amountInBaseUnit: 100, }); ``` ## Complete Workflow ```typescript import { createICTTClient } from "@avalanche-sdk/interchain"; import { avalancheFuji, dispatch } from "@avalanche-sdk/interchain/chains"; const ictt = createICTTClient(avalancheFuji, dispatch); // 1. Deploy token const { contractAddress: tokenAddress } = await ictt.deployERC20Token({ walletClient: wallet, sourceChain: avalancheFuji, name: "My Token", symbol: "MTK", initialSupply: 1000000, }); // 2. Deploy home contract const { contractAddress: tokenHomeAddress } = await ictt.deployTokenHomeContract({ walletClient: wallet, sourceChain: avalancheFuji, erc20TokenAddress: tokenAddress, minimumTeleporterVersion: 1, }); // 3. Deploy remote contract const { contractAddress: tokenRemoteAddress } = await ictt.deployTokenRemoteContract({ walletClient: wallet, sourceChain: avalancheFuji, destinationChain: dispatch, tokenHomeContract: tokenHomeAddress, }); // 4. Register remote with home await ictt.registerRemoteWithHome({ walletClient: wallet, sourceChain: avalancheFuji, destinationChain: dispatch, tokenRemoteContract: tokenRemoteAddress, }); // 5. Approve tokens await ictt.approveToken({ walletClient: wallet, sourceChain: avalancheFuji, tokenHomeContract: tokenHomeAddress, tokenAddress: tokenAddress, amountInBaseUnit: 1000, }); // 6. Send tokens const { txHash } = await ictt.sendToken({ walletClient: wallet, sourceChain: avalancheFuji, destinationChain: dispatch, tokenHomeContract: tokenHomeAddress, tokenRemoteContract: tokenRemoteAddress, recipient: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amountInBaseUnit: 100, }); ``` # JSON-RPC Accounts (/docs/tooling/avalanche-sdk/client/accounts/json-rpc) --- title: JSON-RPC Accounts icon: globe description: Learn how to use JSON-RPC accounts with browser wallets like MetaMask and Core in the Avalanche Client SDK. --- ## Overview A JSON-RPC Account is an Account whose signing keys are stored on an external Wallet. It **defers** signing of transactions & messages to the target Wallet over JSON-RPC. Examples of such wallets include Browser Extension Wallets (like MetaMask or Core) or Mobile Wallets over WalletConnect. ## Supported Wallets ### Core Browser Extension Core is Avalanche's official browser extension wallet that provides native support for Avalanche networks (C/P/X-Chains). ```typescript import "@avalanche-sdk/client/window"; import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; try { // Check if Core extension is available const provider = window.avalanche; if (!provider) { throw new Error( "Core extension not found. Please install Core. https://core.app" ); } // Create wallet client with Core provider const walletClient = createAvalancheWalletClient({ chain: avalanche, transport: { type: "custom", provider, }, }); } catch (error) { console.error("Failed to initialize Core provider:", error); } ``` ### MetaMask MetaMask can be used with Avalanche networks through custom network configuration for C-Chain evm operations. ```typescript import "@avalanche-sdk/client/window"; import { createAvalancheWalletClient, custom } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; // Use MetaMask provider const provider = window.ethereum; if (!provider) { throw new Error("MetaMask not found. Please install MetaMask."); } const walletClient = createAvalancheWalletClient({ chain: avalanche, transport: { type: "custom", provider, }, }); ``` ## Basic Usage ### 1. Request Account Connection ```typescript try { // Request accounts from the wallet const accounts: string[] = await walletClient.requestAddresses(); const address: string = accounts[0]; // Get the first account console.log("Connected address:", address); } catch (error) { console.error("Failed to request addresses:", error); } ``` ### 2. Send Transactions ```typescript try { // Send a transaction (will prompt user to sign) const txHash: string = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: 0.001, }); console.log("Transaction hash:", txHash); } catch (error) { console.error("Failed to send transaction:", error); } ``` ### 3. Switch Networks ```typescript import { avalanche, avalancheFuji } from "@avalanche-sdk/client/chains"; try { // Switch to Avalanche mainnet await walletClient.switchChain({ id: avalanche.id, }); console.log("Switched to Avalanche mainnet"); // Switch to Fuji testnet await walletClient.switchChain({ id: avalancheFuji.id, }); console.log("Switched to Fuji testnet"); } catch (error) { console.error("Failed to switch chain:", error); } ``` ## React Integration Example Here's a complete React component for wallet connection using Core: ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche, avalancheFuji } from "@avalanche-sdk/client/chains"; import { useState, useCallback } from "react"; import "@avalanche-sdk/client/window"; export function ConnectWallet() { const [connected, setConnected] = useState(false); const [address, setAddress] = useState(null); const [chain, setChain] = useState<"mainnet" | "fuji">("fuji"); const selectedChain = chain === "fuji" ? avalancheFuji : avalanche; const connect = useCallback(async () => { try { const provider = window.avalanche; if (!provider) { throw new Error("Core extension not found. Please install Core."); } const walletClient = createAvalancheWalletClient({ chain: selectedChain, transport: { type: "custom", provider }, }); const accounts = await walletClient.requestAddresses(); const addr = accounts[0]; setAddress(addr); setConnected(true); } catch (error) { console.error("Connection failed:", error); } }, [selectedChain]); const sendTransaction = useCallback(async () => { if (!connected || !address) return; try { const provider = (window as any).avalanche; const walletClient = createAvalancheWalletClient({ chain: selectedChain, transport: { type: "custom", provider }, }); const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: 0.001, }); console.log("Transaction sent:", txHash); } catch (error) { console.error("Transaction failed:", error); } }, [connected, address, selectedChain]); return (

Wallet Connection

{!connected ? ( ) : (

Connected: {address}

Network: {selectedChain.name}

)}
); } ``` ## Cross-Chain Operations JSON-RPC accounts support cross-chain operations through the Avalanche Client SDK: ```typescript // P-Chain export transaction const pChainExportTxn = await walletClient.pChain.prepareExportTxn({ destinationChain: "C", fromAddress: address, exportedOutput: { addresses: [address], amount: avaxToWei(0.001), }, }); const txHash = await walletClient.sendXPTransaction(pChainExportTxn); ``` ## Best Practices ### 1. Check Provider Availability ```typescript // Always check if the provider is available if (typeof window !== "undefined" && window.avalanche) { // Core is available } else if (typeof window !== "undefined" && window.ethereum) { // MetaMask is available } else { // No wallet provider found } ``` ### 2. Handle Network Switching ```typescript // Check if wallet is on the correct network const currentChainId = await walletClient.getChainId(); if (currentChainId !== avalanche.id) { await walletClient.switchChain({ id: avalanche.id }); } ``` ### 3. Graceful Error Handling ```typescript const handleWalletError = (error: any) => { switch (error.code) { case 4001: return "User rejected the request"; case -32002: return "Request already pending"; case -32602: return "Invalid parameters"; default: return error.message || "Unknown error occurred"; } }; ``` ## Troubleshooting ### Common Issues **Provider Not Found** ```typescript // Check if provider exists if (!window.avalanche && !window.ethereum) { throw new Error("No wallet provider found. Please install Core or MetaMask."); } ``` **Wrong Network** ```typescript // Ensure wallet is on the correct network const chainId = await walletClient.getChainId(); if (chainId !== avalanche.id) { await walletClient.switchChain({ id: avalanche.id }); } ``` **User Rejection** ```typescript try { await walletClient.send({ to: address, amount: 0.001 }); } catch (error) { if (error.code === 4001) { console.log("User rejected transaction"); } } ``` ## Next Steps - **[Local Accounts](accounts/local)** - Learn about local account management - **[Wallet Operations](methods/wallet-methods/wallet)** - Learn how to send transactions - **[Cross-Chain Transfers](methods/wallet-methods/wallet#cross-chain-transfers)** - Moving assets between chains # Network-Specific Addresses (/docs/tooling/avalanche-sdk/client/accounts/local/addresses) --- title: "Network-Specific Addresses" icon: "map-pin" --- ## Overview Avalanche uses different address formats for each chain. EVM addresses work the same across networks, but XP addresses use network-specific HRPs (Human-Readable Prefixes). ## Address Formats ### EVM Addresses (C-Chain) EVM addresses are the same across all networks: ```typescript const evmAddress = account.getEVMAddress(); // 0x742d35Cc... ``` ### XP Addresses (X/P-Chain) XP addresses use network-specific HRPs: ```typescript // Mainnet const mainnetX = account.getXPAddress("X", "avax"); // X-avax1... const mainnetP = account.getXPAddress("P", "avax"); // P-avax1... // Testnet (Fuji) const fujiX = account.getXPAddress("X", "fuji"); // X-fuji1... const fujiP = account.getXPAddress("P", "fuji"); // P-fuji1... ``` ## Network Configuration ```typescript // Mainnet addresses const mainnet = { evm: account.getEVMAddress(), xChain: account.getXPAddress("X", "avax"), pChain: account.getXPAddress("P", "avax"), }; // Testnet addresses const testnet = { evm: account.getEVMAddress(), xChain: account.getXPAddress("X", "fuji"), pChain: account.getXPAddress("P", "fuji"), }; ``` ## Next Steps - **[Account Utilities](accounts/local/utilities)** - Account validation and utilities - **[Using Accounts with Clients](accounts/local/clients)** - Client integration patterns # Using Accounts with Clients (/docs/tooling/avalanche-sdk/client/accounts/local/clients) --- title: "Using Accounts with Clients" icon: "link" --- ## Overview Accounts work with both public clients (read-only) and wallet clients (transactions). You can hoist the account into the client or pass it to each method. ## Public Client (Read-Only) ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const account = privateKeyToAvalancheAccount("0x..."); // Read operations const balance = await client.getBalance({ address: account.getEVMAddress() }); const height = await client.pChain.getHeight(); ``` ## Wallet Client (Transactions) ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avaxToWei } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); // Hoist account (recommended) const walletClient = createAvalancheWalletClient({ account, // Account is hoisted chain: avalanche, transport: { type: "http" }, }); // C-Chain transaction const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: avaxToWei(0.001), }); // X/P-Chain transaction const xpTx = await walletClient.xChain.prepareBaseTxn({ outputs: [{ addresses: [account.getXPAddress("X")], amount: 1 }], }); await walletClient.sendXPTransaction(xpTx); ``` ## Account Hoisting You can hoist the account into the client (recommended) or pass it to each method: ```typescript // Hoisted (recommended) const walletClient = createAvalancheWalletClient({ account, // No need to pass to each method chain: avalanche, transport: { type: "http" }, }); await walletClient.send({ to: "0x...", amount: 0.001 }); // Or pass per method const walletClient = createAvalancheWalletClient({ chain: avalanche, transport: { type: "http" }, }); await walletClient.send({ account, to: "0x...", amount: 0.001 }); ``` ## Cross-Chain Operations Cross-chain transfers use the export/import pattern. Export from the source chain, wait for confirmation, then import to the destination chain. [Learn more about cross-chain transfers →](methods/wallet-methods/wallet#cross-chain-transfers) ## Next Steps - **[Wallet Operations](methods/wallet-methods/wallet)** - Learn how to send transactions - **[P-Chain Operations](methods/public-methods/p-chain)** - Validator and staking operations - **[X-Chain Operations](methods/public-methods/x-chain)** - Asset transfers and UTXO operations - **[C-Chain Operations](methods/public-methods/c-chain)** - EVM and smart contract operations # HD Key Accounts (/docs/tooling/avalanche-sdk/client/accounts/local/hd-key) --- title: "HD Key Accounts" icon: "tree" --- ## Overview HD Key Accounts create Avalanche accounts from hierarchical deterministic (HD) keys with custom derivation paths. This allows for advanced key management and multiple account generation from a single seed. ## Creating HD Key Accounts ### Basic Usage ```typescript import { hdKeyToAvalancheAccount, HDKey } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const seed: Uint8Array = new Uint8Array(64); // Your seed const hdKey: HDKey = HDKey.fromMasterSeed(seed); const account: AvalancheAccount = hdKeyToAvalancheAccount(hdKey); ``` ### Parameters - **`hdKey: HDKey`** - The HD key instance (required) - **`options?: HDKeyToAvalancheAccountOptions`** - Custom derivation path options (optional) ### Options ```typescript interface HDKeyToAvalancheAccountOptions { accountIndex?: number; // Account index (default: 0) addressIndex?: number; // Address index (default: 0) changeIndex?: number; // Change index (default: 0) xpAccountIndex?: number; // XP account index (default: 0) xpAddressIndex?: number; // XP address index (default: 0) xpChangeIndex?: number; // XP change index (default: 0) path?: string; // Custom derivation path for EVM Account xpPath?: string; // Custom derivation path for XP Account } ``` ## Derivation Paths ### Default Derivation Paths HD Key accounts use BIP-44 derivation paths to generate deterministic keys from a seed. By default, the SDK uses separate paths for EVM (C-Chain) and XP (X/P-Chain) accounts: **EVM (C-Chain) Path:** ``` m/44'/60'/{accountIndex}'/{changeIndex}/{addressIndex} ``` **XP (X/P-Chain) Path:** ``` m/44'/9000'/{xpAccountIndex}'/{xpChangeIndex}/{xpAddressIndex} ``` **Path Components:** - `m` - Master key - `44'` - BIP-44 purpose (hardened) - `60'` (EVM) or `9000'` (XP) - Coin type (hardened) - `60` is the standard Ethereum coin type (used for C-Chain) - `9000` is the Avalanche coin type (used for X/P-Chain) - `{accountIndex}'` - Account index (hardened, default: 0) - `{changeIndex}` - Change index (default: 0) - `0` is typically used for external addresses - `1` is typically used for change addresses - `{addressIndex}` - Address index (default: 0) **Default Values:** When no options are provided, both paths default to `m/44'/60'/0'/0/0` (EVM) and `m/44'/9000'/0'/0/0` (XP). ### How Index Options Affect Paths The following table shows how different index combinations affect the derivation paths: | Option | EVM Path | XP Path | Notes | | ---------------------------------- | ------------------ | -------------------- | ------------------------------- | | Default (no options) | `m/44'/60'/0'/0/0` | `m/44'/9000'/0'/0/0` | Both use index 0 | | `accountIndex: 1` | `m/44'/60'/1'/0/0` | `m/44'/9000'/1'/0/0` | Both use account index 1 | | `addressIndex: 2` | `m/44'/60'/0'/0/2` | `m/44'/9000'/0'/0/2` | Both use address index 2 | | `changeIndex: 1` | `m/44'/60'/0'/1/0` | `m/44'/9000'/0'/1/0` | Both use change index 1 | | `accountIndex: 1, addressIndex: 2` | `m/44'/60'/1'/0/2` | `m/44'/9000'/1'/0/2` | Combined indices | | `xpAccountIndex: 2` | `m/44'/60'/0'/0/0` | `m/44'/9000'/2'/0/0` | XP uses different account index | | `xpAddressIndex: 3` | `m/44'/60'/0'/0/0` | `m/44'/9000'/0'/0/3` | XP uses different address index | | `xpChangeIndex: 1` | `m/44'/60'/0'/0/0` | `m/44'/9000'/0'/1/0` | XP uses different change index | **Important Notes:** - When you specify `accountIndex`, `addressIndex`, or `changeIndex`, they apply to EVM path. - XP-specific options (`xpAccountIndex`, `xpAddressIndex`, `xpChangeIndex`) only affect the XP path, allowing you to use different indices for XP accounts while keeping EVM indices separate. ### Custom Path Override **Important Limitation**: When the `path` or `xpPath` option is provided, it overrides the EVM or XP derivation paths. When you provide a custom `path` or `xpPath`, it completely replaces the default path calculation for EVM and XP accounts: ```typescript import { hdKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; // ⚠️ WARNING: This path will be used for EVM accounts const account: AvalancheAccount = hdKeyToAvalancheAccount(hdKey, { path: "m/44'/60'/0'/0/0", // EVM will use this path }); // ⚠️ WARNING: This path will be used for XP accounts const account: AvalancheAccount = hdKeyToAvalancheAccount(hdKey, { xpPath: "m/44'/60'/0'/0/0", // XP Account will use this path }); // The following options are IGNORED when path is provided: // accountIndex, addressIndex, changeIndex // xpAccountIndex, xpAddressIndex, xpChangeIndex ``` **When to Use Custom Paths:** - When you need to match a specific wallet's derivation path - When migrating from another wallet implementation - When you need full control over the derivation path **When NOT to Use Custom Paths:** - When you want different paths for EVM and XP accounts (use index options instead) - When you want to leverage the default BIP-44 compliant paths - In most standard use cases ### Examples ```typescript import { hdKeyToAvalancheAccount, HDKey } from "@avalanche-sdk/client/accounts"; const hdKey = HDKey.fromMasterSeed(seed); // Default paths (EVM: m/44'/60'/0'/0/0, XP: m/44'/9000'/0'/0/0) const account = hdKeyToAvalancheAccount(hdKey); // Different indices const account1 = hdKeyToAvalancheAccount(hdKey, { accountIndex: 1 }); const account2 = hdKeyToAvalancheAccount(hdKey, { addressIndex: 1 }); // Separate EVM and XP indices const account3 = hdKeyToAvalancheAccount(hdKey, { accountIndex: 0, // EVM xpAccountIndex: 2, // XP }); // Custom path (⚠️ applies to both EVM and XP) const account4 = hdKeyToAvalancheAccount(hdKey, { path: "m/44'/60'/0'/0/0", }); ``` ## Multiple Accounts Generate multiple accounts from the same HD key: ```typescript import { hdKeyToAvalancheAccount, HDKey } from "@avalanche-sdk/client/accounts"; const hdKey = HDKey.fromMasterSeed(seed); const account1 = hdKeyToAvalancheAccount(hdKey, { addressIndex: 0 }); const account2 = hdKeyToAvalancheAccount(hdKey, { addressIndex: 1 }); const account3 = hdKeyToAvalancheAccount(hdKey, { addressIndex: 2 }); ``` ## Using with Wallet Client ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: avaxToWei(0.001), }); ``` ## Security **Never expose seeds in client-side code or commit them to version control. Use environment variables.** ```typescript // ✅ Good const seed = new Uint8Array(Buffer.from(process.env.SEED!, "hex")); const hdKey = HDKey.fromMasterSeed(seed); // ❌ Bad const seed = new Uint8Array(64); // Hardcoded seed ``` ## Next Steps - **[Account Utilities](utilities)** - Account validation and utilities - **[Using Accounts with Clients](clients)** - Client integration patterns # Local Accounts (/docs/tooling/avalanche-sdk/client/accounts/local) --- title: Local Accounts icon: shield description: Learn how to create and manage local accounts in the Avalanche Client SDK with private keys, mnemonics, and HD keys. --- ## Overview Local accounts store keys on your machine and sign transactions before broadcasting. Use these for server-side apps, bots, or when you need full control. **Security:** Never expose private keys or mnemonics in client-side code or commit them to version control. Use environment variables. ## Quick Start ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount(process.env.PRIVATE_KEY!); console.log(account.getEVMAddress()); // 0x742d35Cc... console.log(account.getXPAddress("X")); // X-avax1... ``` ## Account Types ### Private Key Simplest option—create an account directly from a private key. ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); ``` [Private Key Accounts →](local/private-key) ### Mnemonic User-friendly option—create an account from a seed phrase. ```typescript import { mnemonicsToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = mnemonicsToAvalancheAccount("abandon abandon abandon..."); ``` [Mnemonic Accounts →](local/mnemonic) ### HD Key Advanced option—create accounts from HD keys with custom derivation paths. ```typescript import { hdKeyToAvalancheAccount, HDKey } from "@avalanche-sdk/client/accounts"; const hdKey = HDKey.fromMasterSeed(seed); const account = hdKeyToAvalancheAccount(hdKey, { accountIndex: 0 }); ``` [HD Key Accounts →](local/hd-key) ## Instantiation ### Setup Wallet Client ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avaxToWei } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount(process.env.PRIVATE_KEY!); const walletClient = createAvalancheWalletClient({ account, // Hoist account to avoid passing it to each method chain: avalanche, transport: { type: "http" }, }); // Use wallet methods const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: avaxToWei(0.001), }); // Use public methods const balance = await walletClient.getBalance({ address: account.getEVMAddress(), }); ``` ## Account Generation ### Generate Private Key ```typescript import { generatePrivateKey, privateKeyToAvalancheAccount, } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const privateKey: string = generatePrivateKey(); const account: AvalancheAccount = privateKeyToAvalancheAccount(privateKey); ``` ### Generate Mnemonic ```typescript import { generateMnemonic, mnemonicsToAvalancheAccount, } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const mnemonic: string = generateMnemonic(); const account: AvalancheAccount = mnemonicsToAvalancheAccount(mnemonic); ``` ## Address Management ### Get All Addresses ```typescript const addresses = { evm: account.getEVMAddress(), xChain: account.getXPAddress("X"), pChain: account.getXPAddress("P"), }; console.log("EVM Address:", addresses.evm); console.log("X-Chain Address:", addresses.xChain); console.log("P-Chain Address:", addresses.pChain); ``` **Learn more:** For network-specific addresses and detailed address management examples, see [Network-Specific Addresses](local/addresses). ## Security **Never expose private keys or mnemonics in client-side code or commit them to version control. Always use environment variables.** ```typescript // ✅ Good: Use environment variables const account = privateKeyToAvalancheAccount(process.env.PRIVATE_KEY!); // ❌ Bad: Hardcoded private key const account = privateKeyToAvalancheAccount("0x1234..."); ``` ## Learn More - [Account Generation](#account-generation) - Create secure accounts - [Address Management](#address-management) - Multi-network addresses - [Using with Clients](local/clients) - Client integration - [HD Key Accounts](local/hd-key) - Hierarchical deterministic accounts - [Account Utilities](local/utilities) - Validation and helpers - [Network-Specific Addresses](local/addresses) - Advanced address management # Mnemonic Accounts (/docs/tooling/avalanche-sdk/client/accounts/local/mnemonic) --- title: "Mnemonic Accounts" icon: "seedling" --- ## Overview Mnemonic Accounts create Avalanche accounts from mnemonic phrases (seed words). Mnemonics provide a human-readable way to backup and restore accounts, making them ideal for user-facing applications. **Best for:** - User-facing applications - Mobile wallets - Desktop wallets - Applications requiring easy account recovery ## Creating Mnemonic Accounts ### Basic Usage ```typescript import { mnemonicsToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; const account: AvalancheAccount = mnemonicsToAvalancheAccount(mnemonic); console.log("EVM Address:", account.getEVMAddress()); console.log("X-Chain Address:", account.getXPAddress("X")); console.log("P-Chain Address:", account.getXPAddress("P")); ``` ### Parameters - **`mnemonic: string`** - The mnemonic phrase (required) - **`options?: MnemonicToAccountOptions`** - Optional derivation path configuration ### Options Interface ```typescript interface MnemonicToAccountOptions { accountIndex?: number; // Account index (default: 0) addressIndex?: number; // Address index (default: 0) changeIndex?: number; // Change index (default: 0) xpAccountIndex?: number; // XP account index (default: 0) xpAddressIndex?: number; // XP address index (default: 0) xpChangeIndex?: number; // XP change index (default: 0) path?: string; // Custom derivation path for EVM Account xpPath?: string; // Custom derivation path for XP Account } ``` ## Generating Mnemonics ### Generate Random Mnemonic ```typescript import { generateMnemonic, mnemonicsToAvalancheAccount, } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const mnemonic: string = generateMnemonic(); console.log("Generated mnemonic:", mnemonic); const account: AvalancheAccount = mnemonicsToAvalancheAccount(mnemonic); console.log("EVM address:", account.getEVMAddress()); console.log("X-Chain address:", account.getXPAddress("X")); console.log("P-Chain address:", account.getXPAddress("P")); // ⚠️ IMPORTANT: Store mnemonic securely // Never log it in production or commit to version control ``` ### Generate Mnemonic in Different Languages ```typescript import { generateMnemonic, english, spanish, japanese, } from "@avalanche-sdk/client/accounts"; // Generate mnemonic in different languages const englishMnemonic = generateMnemonic(english); const spanishMnemonic = generateMnemonic(spanish); const japaneseMnemonic = generateMnemonic(japanese); // Also available: french, italian, portuguese, czech, korean, simplifiedChinese, traditionalChinese ``` ## Derivation Paths ### Default Derivation Paths The Avalanche Client SDK uses different derivation paths for EVM and XP accounts: ```typescript // EVM (C-Chain) derivation path // Standard BIP44 path for Ethereum const evmPath = "m/44'/60'/0'/0/0"; // m/44'/60'/{accountIndex}'/{changeIndex}/{addressIndex} // XP (X/P-Chain) derivation path // Standard BIP44 path for Avalanche const xpPath = "m/44'/9000'/0'/0/0"; // m/44'/9000'/{xpAccountIndex}'/{xpChangeIndex}/{xpAddressIndex} ``` ### Custom Derivation Paths ```typescript import { mnemonicsToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const mnemonic = "abandon abandon abandon..."; // Create account with custom derivation paths const account: AvalancheAccount = mnemonicsToAvalancheAccount(mnemonic, { accountIndex: 0, addressIndex: 0, changeIndex: 0, path: "m/44'/60'/0'/0/0", // Custom path }); ``` ### Multiple Accounts from Same Mnemonic ```typescript import { mnemonicsToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const mnemonic = "abandon abandon abandon..."; // Different account indices const account0 = mnemonicsToAvalancheAccount(mnemonic, { accountIndex: 0 }); const account1 = mnemonicsToAvalancheAccount(mnemonic, { accountIndex: 1 }); // Different address indices const addr0 = mnemonicsToAvalancheAccount(mnemonic, { addressIndex: 0 }); const addr1 = mnemonicsToAvalancheAccount(mnemonic, { addressIndex: 1 }); ``` ## Getting Addresses ```typescript const account = mnemonicsToAvalancheAccount(mnemonic); // All chain addresses const evmAddress = account.getEVMAddress(); const xChainAddress = account.getXPAddress("X"); const pChainAddress = account.getXPAddress("P"); // Network-specific const mainnet = account.getXPAddress("X", "avax"); const testnet = account.getXPAddress("X", "fuji"); ``` ## Using with Wallet Client ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; import { mnemonicsToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = mnemonicsToAvalancheAccount(process.env.MNEMONIC!); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // C-Chain transaction const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: 0.001, }); // X/P-Chain transaction const xpTx = await walletClient.xChain.prepareBaseTxn({ outputs: [{ addresses: [account.getXPAddress("X")], amount: 1 }], }); await walletClient.sendXPTransaction(xpTx); ``` ## Security **Never expose mnemonics in client-side code or commit them to version control. Use environment variables.** ```typescript // ✅ Good const account = mnemonicsToAvalancheAccount(process.env.MNEMONIC!); // ❌ Bad const account = mnemonicsToAvalancheAccount("abandon abandon abandon..."); ``` ## Next Steps - **[HD Key Accounts](accounts/local/hd-key)** - Learn about hierarchical deterministic accounts - **[Account Utilities](accounts/local/utilities)** - Account validation and utilities - **[Using Accounts with Clients](accounts/local/clients)** - Client integration patterns - **[Network-Specific Addresses](accounts/local/addresses)** - Multi-network support # Private Key Accounts (/docs/tooling/avalanche-sdk/client/accounts/local/private-key) --- title: "Private Key Accounts" icon: "key" --- ## Overview Private Key Accounts provide the simplest way to create an Avalanche account from a single private key. They support both EVM (C-Chain) and X/P (X-Chain/P-Chain) operations with unified address management. **Best for:** - Server-side applications - Automated bots and services - Testing and development - Scripts and tools **Security:** Private keys must be kept secure. Never expose private keys in client-side code or commit them to version control. Use environment variables in production. ## Creating Private Key Accounts ### Basic Usage ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const privateKey = "0x1234...your_private_key_here"; const account: AvalancheAccount = privateKeyToAvalancheAccount(privateKey); console.log("EVM Address:", account.getEVMAddress()); console.log("X-Chain Address:", account.getXPAddress("X")); console.log("P-Chain Address:", account.getXPAddress("P")); ``` ### Working with Environment Variables ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount(process.env.PRIVATE_KEY!); ``` ## Generating Private Keys ### Generate Random Private Key ```typescript import { generatePrivateKey, privateKeyToAvalancheAccount, } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const privateKey: string = generatePrivateKey(); console.log("Generated private key:", privateKey); const account: AvalancheAccount = privateKeyToAvalancheAccount(privateKey); console.log("EVM Address:", account.getEVMAddress()); console.log("X-Chain Address:", account.getXPAddress("X")); console.log("P-Chain Address:", account.getXPAddress("P")); ``` ## Account Properties ### EVM Account Each Avalanche account contains an EVM account for C-Chain operations: ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const account: AvalancheAccount = privateKeyToAvalancheAccount("0x..."); // Access EVM account properties const evmAccount = account.evmAccount; console.log("Address:", evmAccount.address); console.log("Type:", evmAccount.type); // "local" console.log("Source:", evmAccount.source); // "privateKey" // Get public key (if available) if (evmAccount.publicKey) { console.log("Public Key:", evmAccount.publicKey); } ``` ### XP Account Each Avalanche account contains an XP account for X-Chain and P-Chain operations: ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount, LocalXPAccount, } from "@avalanche-sdk/client/accounts"; const account: AvalancheAccount = privateKeyToAvalancheAccount("0x..."); // Access XP account properties if (account.xpAccount) { const xpAccount: LocalXPAccount = account.xpAccount; console.log("Public Key:", xpAccount.publicKey); console.log("Type:", xpAccount.type); // "local" console.log("Source:", xpAccount.source); // "privateKey" } ``` ## Address Management ### Get All Addresses ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const account: AvalancheAccount = privateKeyToAvalancheAccount("0x..."); // Get all addresses const addresses = { evm: account.getEVMAddress(), // "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6" xChain: account.getXPAddress("X"), // "X-avax1..." pChain: account.getXPAddress("P"), // "P-avax1..." base: account.getXPAddress(), // "avax1..." (without chain prefix) }; console.log("All Addresses:", addresses); ``` ### Network-Specific Addresses ```typescript import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount } from "@avalanche-sdk/client/accounts"; const account: AvalancheAccount = privateKeyToAvalancheAccount("0x..."); // Mainnet addresses (default) const mainnetAddresses = { evm: account.getEVMAddress(), xChain: account.getXPAddress("X", "avax"), pChain: account.getXPAddress("P", "avax"), }; // Testnet (Fuji) addresses const testnetAddresses = { evm: account.getEVMAddress(), xChain: account.getXPAddress("X", "fuji"), pChain: account.getXPAddress("P", "fuji"), }; console.log("Mainnet:", mainnetAddresses); console.log("Testnet:", testnetAddresses); ``` ## Using with Wallet Client ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount(process.env.PRIVATE_KEY!); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // C-Chain transaction const txHash = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: 0.001, }); // X/P-Chain transaction const xpTx = await walletClient.xChain.prepareBaseTxn({ outputs: [{ addresses: [account.getXPAddress("X")], amount: 1 }], }); await walletClient.sendXPTransaction(xpTx); ``` ## Message Signing ```typescript import { signMessage } from "@avalanche-sdk/client/accounts"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; const account = privateKeyToAvalancheAccount("0x..."); // EVM message signing const evmSignature = await signMessage({ account: account.evmAccount, message: "Hello Avalanche!", }); // XP message signing if (account.xpAccount) { const xpSignature = await account.xpAccount.signMessage("Hello Avalanche!"); const isValid = account.xpAccount.verify("Hello Avalanche!", xpSignature); } ``` ## Security **Never expose private keys in client-side code or commit them to version control. Use environment variables.** ```typescript // ✅ Good const account = privateKeyToAvalancheAccount(process.env.PRIVATE_KEY!); // ❌ Bad const account = privateKeyToAvalancheAccount("0x1234..."); ``` ## Next Steps - **[Mnemonic Accounts](mnemonic)** - Learn about mnemonic-based accounts - **[HD Key Accounts](hd-key)** - Learn about hierarchical deterministic accounts - **[Account Utilities](utilities)** - Account validation and utilities - **[Network-Specific Addresses](addresses)** - Working with different network addresses # Account Utilities (/docs/tooling/avalanche-sdk/client/accounts/local/utilities) --- title: "Account Utilities" icon: "hammer" --- ## Overview Account utilities provide validation, parsing, and helper functions for working with Avalanche accounts and addresses. ## Account Validation ### Parse Avalanche Account ```typescript function parseAvalancheAccount( account: Address | AvalancheAccount | undefined ): AvalancheAccount | undefined; ``` **Parameters:** - `account` (`Address | AvalancheAccount | undefined`): The account or address to parse. Can be an EVM address string, an existing `AvalancheAccount`, or `undefined`. **Returns:** - `AvalancheAccount | undefined`: Returns an `AvalancheAccount` when an address or account is provided, or `undefined` when the input is `undefined`. **Behavior:** The function behaves differently based on the input type: 1. **When an address string is passed**: Returns an `AvalancheAccount` with only `evmAccount` populated. The `xpAccount` property will be `undefined` because an address string alone doesn't contain the private key information needed to derive XP account details. 2. **When an AvalancheAccount is passed**: Returns the account as-is without modification. This is useful for normalizing function parameters that accept both addresses and accounts. 3. **When undefined is passed**: Returns `undefined`. This allows for optional account parameters in functions. **Limitations:** - When parsing from an address string, the returned account will only have `evmAccount` populated. The `xpAccount` property will be `undefined`, which means: - You cannot perform X-Chain or P-Chain operations that require signing (e.g., `account.xpAccount.signMessage()`) - You can still use the account for read-only operations or C-Chain (EVM) operations - To get XP account functionality, you need to create an account from a private key or mnemonic or use a custom provider **Example:** ```typescript import { parseAvalancheAccount } from "@avalanche-sdk/client/accounts"; import type { AvalancheAccount, Address } from "@avalanche-sdk/client/accounts"; // Parse from address string (only evmAccount populated) const address: Address = "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6"; const account = parseAvalancheAccount(address); // account.xpAccount is undefined - can only use for C-Chain operations // Parse existing account (returns as-is) const fullAccount: AvalancheAccount = /* ... */; const normalized = parseAvalancheAccount(fullAccount); // Returns as-is // Handle undefined const optional = parseAvalancheAccount(undefined); // Returns undefined ``` ## Address Utilities ### Private Key to XP Address ```typescript function privateKeyToXPAddress(privateKey: string, hrp: string): XPAddress; ``` **Parameters:** - `privateKey` (`string`): The private key with `0x` prefix. - `hrp` (`string`): The human-readable prefix for the address. Use `"avax"` for mainnet or `"fuji"` for testnet. **Returns:** - `XPAddress`: The Bech32-encoded XP address as a string. **Example:** ```typescript import { privateKeyToXPAddress } from "@avalanche-sdk/client/accounts"; const mainnet = privateKeyToXPAddress("0x...", "avax"); const testnet = privateKeyToXPAddress("0x...", "fuji"); ``` ### Public Key to XP Address ```typescript function publicKeyToXPAddress(publicKey: string, hrp: string): XPAddress; ``` **Parameters:** - `publicKey` (`string`): The public key with `0x` prefix. - `hrp` (`string`): The human-readable prefix for the address. Use `"avax"` for mainnet or `"fuji"` for testnet. **Returns:** - `XPAddress`: The Bech32-encoded XP address as a string. **Example:** ```typescript import { publicKeyToXPAddress } from "@avalanche-sdk/client/accounts"; const xpAddress = publicKeyToXPAddress("0x...", "avax"); ``` ### Private Key to XP Public Key ```typescript function privateKeyToXPPublicKey(privateKey: string): string; ``` **Parameters:** - `privateKey` (`string`): The private key with `0x` prefix. **Returns:** - `string`: The compressed public key in hex format with `0x` prefix. **Example:** ```typescript import { privateKeyToXPPublicKey, publicKeyToXPAddress, } from "@avalanche-sdk/client/accounts"; const publicKey = privateKeyToXPPublicKey("0x..."); const xpAddress = publicKeyToXPAddress(publicKey, "avax"); ``` ## Message Signing ### XP Message Signing ```typescript function xpSignMessage(message: string, privateKey: string): Promise; ``` **Parameters:** - `message` (`string`): The message to sign. - `privateKey` (`string`): The private key with `0x` prefix to sign with. **Returns:** - `Promise`: A promise that resolves to a base58-encoded signature string. **Example:** ```typescript import { xpSignMessage } from "@avalanche-sdk/client/accounts"; const signature = await xpSignMessage("Hello Avalanche!", "0x..."); // Returns base58-encoded signature (e.g., "2k5Jv...") ``` ### XP Transaction Signing ```typescript function xpSignTransaction( txHash: string | Uint8Array, privateKey: string | Uint8Array ): Promise; ``` **Parameters:** - `txHash` (`string | Uint8Array`): The transaction hash to sign. Can be a hex string with `0x` prefix or a `Uint8Array`. - `privateKey` (`string | Uint8Array`): The private key to sign with. Can be a hex string with `0x` prefix or a `Uint8Array`. **Returns:** - `Promise`: A promise that resolves to a hex-encoded signature string with `0x` prefix. **Example:** ```typescript import { xpSignTransaction } from "@avalanche-sdk/client/accounts"; const signature = await xpSignTransaction("0x...", "0x..."); // Returns hex-encoded signature (e.g., "0x1234...") ``` ### Signature Verification ```typescript function xpVerifySignature( signature: string, message: string, publicKey: string ): boolean; ``` **Parameters:** - `signature` (`string`): The signature to verify in hex format with `0x` prefix. - `message` (`string`): The message that was signed. - `publicKey` (`string`): The public key to verify with in hex format with `0x` prefix. **Returns:** - `boolean`: `true` if the signature is valid, `false` otherwise. **Note:** This function expects hex format signatures. For base58 signatures created with `xpSignMessage`, use `xpAccount.verify()` instead. **Example:** ```typescript import { xpVerifySignature } from "@avalanche-sdk/client/accounts"; const isValid = xpVerifySignature("0x...", "Hello Avalanche!", "0x..."); // For base58 signatures from xpSignMessage, use xpAccount.verify() instead ``` ### Public Key Recovery ```typescript function xpRecoverPublicKey(message: string, signature: string): string; ``` **Parameters:** - `message` (`string`): The message that was signed. - `signature` (`string`): The signature in hex format with `0x` prefix. **Returns:** - `string`: The recovered public key as a hex string with `0x` prefix. **Example:** ```typescript import { xpRecoverPublicKey } from "@avalanche-sdk/client/accounts"; const publicKey = xpRecoverPublicKey("Hello Avalanche!", "0x..."); ``` ## XP Account Creation ### Private Key to XP Account ```typescript function privateKeyToXPAccount(privateKey: string): XPAccount; ``` **Parameters:** - `privateKey` (`string`): The private key with `0x` prefix. **Returns:** - `XPAccount`: An XP account object with the following properties: - `publicKey` (`string`): The compressed public key in hex format. - `signMessage(message: string)` (`Promise`): Signs a message and returns a base58-encoded signature. - `signTransaction(txHash: string | Uint8Array)` (`Promise`): Signs a transaction hash and returns a hex-encoded signature. - `verify(message: string, signature: string)` (`boolean`): Verifies a message signature. - `type` (`"local"`): The account type. - `source` (`"privateKey"`): The account source. **Note:** Creates an XP-only account from a private key. Lighter weight than `privateKeyToAvalancheAccount` since it skips EVM account initialization. Use this when you only need X-Chain or P-Chain operations. **Limitations**: No EVM account—can't use for C-Chain operations. If you need both XP and EVM functionality, use `privateKeyToAvalancheAccount` instead. **Example:** ```typescript import { privateKeyToXPAccount } from "@avalanche-sdk/client/accounts"; const xpAccount = privateKeyToXPAccount("0x..."); // Sign message const signature = await xpAccount.signMessage("Hello Avalanche!"); const isValid = xpAccount.verify("Hello Avalanche!", signature); // Sign transaction const txSignature = await xpAccount.signTransaction("0x..."); ``` ## Next Steps - **[Using Accounts with Clients](accounts/local/clients)** - Client integration patterns - **[Wallet Operations](methods/wallet-methods/wallet)** - Learn how to send transactions - **[Account Management](accounts)** - Overview of account management # API Methods (/docs/tooling/avalanche-sdk/client/methods/public-methods/api) --- title: API Methods description: Complete reference for Admin, Info, Health, Index, and ProposerVM API methods --- ## Overview The Avalanche Client SDK provides access to node-level API methods through specialized API clients. These include administrative operations, informational queries, health monitoring, indexed blockchain data access, and ProposerVM operations. ## Admin API Client Provides administrative operations for managing node aliases, logging, and profiling. **Access:** `client.admin` ### alias Assign an API endpoint an alias. **Function Signature:** ```typescript function alias(params: AliasParameters): Promise; interface AliasParameters { endpoint: string; alias: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ---------------------- | | `endpoint` | `string` | Yes | API endpoint to alias | | `alias` | `string` | Yes | Alias for the endpoint | **Returns:** | Type | Description | | --------------- | --------------- | | `Promise` | No return value | **Example:** ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); await client.admin.alias({ endpoint: "bc/X", alias: "myAlias", }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/admin-rpc#adminalias) --- ### aliasChain Give a blockchain an alias. **Function Signature:** ```typescript function aliasChain(params: AliasChainParameters): Promise; interface AliasChainParameters { chain: string; alias: string; } ``` **Parameters:** | Name | Type | Required | Description | | ------- | -------- | -------- | ------------------------ | | `chain` | `string` | Yes | Blockchain ID to alias | | `alias` | `string` | Yes | Alias for the blockchain | **Returns:** | Type | Description | | --------------- | --------------- | | `Promise` | No return value | **Example:** ```typescript await client.admin.aliasChain({ chain: "sV6o671RtkGBcno1FiaDbVcFv2sG5aVXMZYzKdP4VQAWmJQnM", alias: "myBlockchainAlias", }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/admin-rpc#adminaliaschain) --- ### getChainAliases Get the aliases of a chain. **Function Signature:** ```typescript function getChainAliases(params: GetChainAliasesParameters): Promise; interface GetChainAliasesParameters { chain: string; } ``` **Parameters:** | Name | Type | Required | Description | | ------- | -------- | -------- | ---------------------- | | `chain` | `string` | Yes | Blockchain ID to query | **Returns:** | Type | Description | | ------------------- | ------------------------------ | | `Promise` | Array of aliases for the chain | **Example:** ```typescript const aliases = await client.admin.getChainAliases({ chain: "sV6o671RtkGBcno1FiaDbVcFv2sG5aVXMZYzKdP4VQAWmJQnM", }); console.log("Chain aliases:", aliases); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/admin-rpc#admingetchainaliases) --- ### Additional Admin API Methods - **getLoggerLevel** - Get log and display levels of loggers - **setLoggerLevel** - Set log and display levels of loggers - **loadVMs** - Dynamically loads virtual machines - **lockProfile** - Writes mutex statistics to `lock.profile` - **memoryProfile** - Writes memory profile to `mem.profile` - **startCPUProfiler** - Start CPU profiling - **stopCPUProfiler** - Stop CPU profiler See the [Admin API documentation](clients/api-clients#admin-api-client) for complete details. --- ## Info API Client Provides node information and network statistics. **Access:** `client.info` ### getNetworkID Get the ID of the network this node is participating in. **Function Signature:** ```typescript function getNetworkID(): Promise; interface GetNetworkIDReturnType { networkID: string; } ``` **Returns:** | Type | Description | | ------------------------ | ----------------- | | `GetNetworkIDReturnType` | Network ID object | **Return Object:** | Property | Type | Description | | ----------- | -------- | ---------------------------------------------- | | `networkID` | `string` | Network ID (1 for Mainnet, 5 for Fuji testnet) | **Example:** ```typescript const result = await client.info.getNetworkID(); if (result.networkID === "1") { console.log("Connected to Mainnet"); } else if (result.networkID === "5") { console.log("Connected to Fuji testnet"); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogetnetworkid) --- ### getNetworkName Get the name of the network this node is participating in. **Function Signature:** ```typescript function getNetworkName(): Promise; interface GetNetworkNameReturnType { networkName: string; } ``` **Returns:** | Type | Description | | -------------------------- | ------------------- | | `GetNetworkNameReturnType` | Network name object | **Return Object:** | Property | Type | Description | | ------------- | -------- | -------------------------------------- | | `networkName` | `string` | Network name (e.g., "mainnet", "fuji") | **Example:** ```typescript const result = await client.info.getNetworkName(); console.log("Network:", result.networkName); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogetnetworkname) --- ### getNodeVersion Get the version of this node. **Function Signature:** ```typescript function getNodeVersion(): Promise; interface GetNodeVersionReturnType { version: string; databaseVersion: string; gitCommit: string; vmVersions: Map; rpcProtocolVersion: string; } ``` **Returns:** | Type | Description | | -------------------------- | ------------------- | | `GetNodeVersionReturnType` | Node version object | **Return Object:** | Property | Type | Description | | -------------------- | --------------------- | -------------------------------------- | | `version` | `string` | Node version (e.g., "avalanche/1.9.4") | | `databaseVersion` | `string` | Database version | | `gitCommit` | `string` | Git commit hash | | `vmVersions` | `Map` | Map of VM IDs to their versions | | `rpcProtocolVersion` | `string` | RPC protocol version | **Example:** ```typescript const version = await client.info.getNodeVersion(); console.log("Node version:", version.version); console.log("Database version:", version.databaseVersion); console.log("VM versions:", Object.fromEntries(version.vmVersions)); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogetnodeversion) --- ### getNodeID Get the node ID, BLS key, and proof of possession. **Function Signature:** ```typescript function getNodeID(): Promise; interface GetNodeIDReturnType { nodeID: string; nodePOP: { publicKey: string; proofOfPossession: string; }; } ``` **Returns:** | Type | Description | | --------------------- | -------------- | | `GetNodeIDReturnType` | Node ID object | **Return Object:** | Property | Type | Description | | --------------------------- | -------- | ----------------------------------------------- | | `nodeID` | `string` | Unique identifier of the node | | `nodePOP.publicKey` | `string` | 48 byte hex representation of the BLS key | | `nodePOP.proofOfPossession` | `string` | 96 byte hex representation of the BLS signature | **Example:** ```typescript const nodeID = await client.info.getNodeID(); console.log("Node ID:", nodeID.nodeID); console.log("BLS Public Key:", nodeID.nodePOP.publicKey); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogetnodeid) --- ### getNodeIP Get the IP address of the node. **Function Signature:** ```typescript function getNodeIP(): Promise; interface GetNodeIPReturnType { ip: string; } ``` **Returns:** | Type | Description | | --------------------- | -------------- | | `GetNodeIPReturnType` | Node IP object | **Return Object:** | Property | Type | Description | | -------- | -------- | ---------------------- | | `ip` | `string` | IP address of the node | **Example:** ```typescript const result = await client.info.getNodeIP(); console.log("Node IP:", result.ip); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogetnodeip) --- ### getBlockchainID Get blockchain ID from alias. **Function Signature:** ```typescript function getBlockchainID( params: GetBlockchainIDParameters ): Promise; interface GetBlockchainIDParameters { alias: string; } interface GetBlockchainIDReturnType { blockchainID: string; } ``` **Parameters:** | Name | Type | Required | Description | | ------- | -------- | -------- | --------------------------------- | | `alias` | `string` | Yes | Blockchain alias (e.g., "X", "P") | **Returns:** | Type | Description | | --------------------------- | -------------------- | | `GetBlockchainIDReturnType` | Blockchain ID object | **Return Object:** | Property | Type | Description | | -------------- | -------- | -------------------- | | `blockchainID` | `string` | ID of the blockchain | **Example:** ```typescript const result = await client.info.getBlockchainID({ alias: "X" }); console.log("X-Chain ID:", result.blockchainID); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogetblockchainid) --- ### getTxFee Get transaction fees for various operations. **Function Signature:** ```typescript function getTxFee(): Promise; interface GetTxFeeReturnType { txFee: bigint; createAssetTxFee: bigint; createSubnetTxFee: bigint; transformSubnetTxFee: bigint; createBlockchainTxFee: bigint; addPrimaryNetworkValidatorFee: bigint; addPrimaryNetworkDelegatorFee: bigint; addSubnetValidatorFee: bigint; addSubnetDelegatorFee: bigint; } ``` **Returns:** | Type | Description | | -------------------- | ----------------------- | | `GetTxFeeReturnType` | Transaction fees object | **Return Object:** | Property | Type | Description | | ------------------------------- | -------- | ------------------------------------------ | | `txFee` | `bigint` | Base transaction fee | | `createAssetTxFee` | `bigint` | Fee for creating an asset | | `createSubnetTxFee` | `bigint` | Fee for creating a subnet | | `transformSubnetTxFee` | `bigint` | Fee for transforming a subnet | | `createBlockchainTxFee` | `bigint` | Fee for creating a blockchain | | `addPrimaryNetworkValidatorFee` | `bigint` | Fee for adding a primary network validator | | `addPrimaryNetworkDelegatorFee` | `bigint` | Fee for adding a primary network delegator | | `addSubnetValidatorFee` | `bigint` | Fee for adding a subnet validator | | `addSubnetDelegatorFee` | `bigint` | Fee for adding a subnet delegator | **Example:** ```typescript const fees = await client.info.getTxFee(); console.log("Base transaction fee:", fees.txFee); console.log("Create asset fee:", fees.createAssetTxFee); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogettxfee) --- ### getVMs Get supported virtual machines. **Function Signature:** ```typescript function getVMs(): Promise; interface GetVMsReturnType { vms: { [key: string]: string[]; }; } ``` **Returns:** | Type | Description | | ------------------ | ----------- | | `GetVMsReturnType` | VMs object | **Return Object:** | Property | Type | Description | | -------- | ----------------------------- | ------------------------------ | | `vms` | `{ [key: string]: string[] }` | Map of VM IDs to their aliases | **Example:** ```typescript const vms = await client.info.getVMs(); console.log("Supported VMs:", vms.vms); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infogetvms) --- ### isBootstrapped Check whether a given chain is done bootstrapping. **Function Signature:** ```typescript function isBootstrapped( params: IsBootstrappedParameters ): Promise; interface IsBootstrappedParameters { chain: string; } interface IsBootstrappedReturnType { isBootstrapped: boolean; } ``` **Parameters:** | Name | Type | Required | Description | | ------- | -------- | -------- | ----------------------------- | | `chain` | `string` | Yes | Chain ID or alias (e.g., "X") | **Returns:** | Type | Description | | -------------------------- | ------------------- | | `IsBootstrappedReturnType` | Bootstrapped status | **Return Object:** | Property | Type | Description | | ---------------- | --------- | --------------------------------------- | | `isBootstrapped` | `boolean` | Whether the chain is done bootstrapping | **Example:** ```typescript const result = await client.info.isBootstrapped({ chain: "X" }); if (result.isBootstrapped) { console.log("X-Chain is bootstrapped"); } else { console.log("X-Chain is still bootstrapping"); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infoisbootstrapped) --- ### peers Get peer information. **Function Signature:** ```typescript function peers(params: PeersParameters): Promise; interface PeersParameters { nodeIDs?: string[]; } interface PeersReturnType { numPeers: number; peers: { ip: string; publicIP: string; nodeID: string; version: string; lastSent: string; lastReceived: string; benched: string[]; observedUptime: number; }[]; } ``` **Parameters:** | Name | Type | Required | Description | | --------- | ---------- | -------- | ------------------------------------ | | `nodeIDs` | `string[]` | No | Optional array of node IDs to filter | **Returns:** | Type | Description | | ----------------- | ------------ | | `PeersReturnType` | Peers object | **Return Object:** | Property | Type | Description | | ---------- | -------- | --------------------------------- | | `numPeers` | `number` | Number of connected peers | | `peers` | `array` | Array of peer information objects | **Peer Object:** | Property | Type | Description | | ---------------- | ---------- | -------------------------------------------------- | | `ip` | `string` | Remote IP of the peer | | `publicIP` | `string` | Public IP of the peer | | `nodeID` | `string` | Prefixed Node ID of the peer | | `version` | `string` | Version the peer is running | | `lastSent` | `string` | Timestamp of last message sent to the peer | | `lastReceived` | `string` | Timestamp of last message received from the peer | | `benched` | `string[]` | Array of chain IDs the peer is benched on | | `observedUptime` | `number` | Node's primary network uptime observed by the peer | **Example:** ```typescript const peers = await client.info.peers(); console.log("Number of peers:", peers.numPeers); console.log("Peer details:", peers.peers); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infopeers) --- ### uptime Get node uptime statistics. **Function Signature:** ```typescript function uptime(): Promise; interface UptimeReturnType { rewardingStakePercentage: number; weightedAveragePercentage: number; } ``` **Returns:** | Type | Description | | ------------------ | ------------- | | `UptimeReturnType` | Uptime object | **Return Object:** | Property | Type | Description | | --------------------------- | -------- | ----------------------------------------------------------------------- | | `rewardingStakePercentage` | `number` | Percent of stake which thinks this node is above the uptime requirement | | `weightedAveragePercentage` | `number` | Stake-weighted average of all observed uptimes for this node | **Example:** ```typescript const uptime = await client.info.uptime(); console.log("Rewarding stake percentage:", uptime.rewardingStakePercentage); console.log("Weighted average percentage:", uptime.weightedAveragePercentage); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infouptime) --- ### upgrades Get upgrade history. **Function Signature:** ```typescript function upgrades(): Promise; interface UpgradesReturnType { apricotPhase1Time: string; apricotPhase2Time: string; apricotPhase3Time: string; apricotPhase4Time: string; apricotPhase4MinPChainHeight: number; apricotPhase5Time: string; apricotPhasePre6Time: string; apricotPhase6Time: string; apricotPhasePost6Time: string; banffTime: string; cortinaTime: string; cortinaXChainStopVertexID: string; durangoTime: string; etnaTime: string; fortunaTime?: string; } ``` **Returns:** | Type | Description | | -------------------- | --------------- | | `UpgradesReturnType` | Upgrades object | **Return Object:** | Property | Type | Description | | ------------------------------ | --------- | ------------------------------------------ | | `apricotPhase1Time` | `string` | Timestamp of Apricot Phase 1 upgrade | | `apricotPhase2Time` | `string` | Timestamp of Apricot Phase 2 upgrade | | `apricotPhase3Time` | `string` | Timestamp of Apricot Phase 3 upgrade | | `apricotPhase4Time` | `string` | Timestamp of Apricot Phase 4 upgrade | | `apricotPhase4MinPChainHeight` | `number` | Minimum P-Chain height for Apricot Phase 4 | | `apricotPhase5Time` | `string` | Timestamp of Apricot Phase 5 upgrade | | `apricotPhasePre6Time` | `string` | Timestamp of Apricot Phase Pre-6 upgrade | | `apricotPhase6Time` | `string` | Timestamp of Apricot Phase 6 upgrade | | `apricotPhasePost6Time` | `string` | Timestamp of Apricot Phase Post-6 upgrade | | `banffTime` | `string` | Timestamp of Banff upgrade | | `cortinaTime` | `string` | Timestamp of Cortina upgrade | | `cortinaXChainStopVertexID` | `string` | X-Chain stop vertex ID for Cortina upgrade | | `durangoTime` | `string` | Timestamp of Durango upgrade | | `etnaTime` | `string` | Timestamp of Etna upgrade | | `fortunaTime` | `string?` | Timestamp of Fortuna upgrade (optional) | **Example:** ```typescript const upgrades = await client.info.upgrades(); console.log("Apricot Phase 1:", upgrades.apricotPhase1Time); console.log("Banff upgrade:", upgrades.banffTime); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infoupgrades) --- ### acps Get peer preferences for Avalanche Community Proposals. **Function Signature:** ```typescript function acps(): Promise; interface AcpsReturnType { acps: Map< number, { supportWeight: bigint; supporters: Set; objectWeight: bigint; objectors: Set; abstainWeight: bigint; } >; } ``` **Returns:** | Type | Description | | ---------------- | ----------- | | `AcpsReturnType` | ACPs object | **Return Object:** | Property | Type | Description | | -------- | ----- | ---------------------------------------- | | `acps` | `Map` | Map of ACP IDs to their peer preferences | **ACP Object:** | Property | Type | Description | | --------------- | ------------- | --------------------------------------- | | `supportWeight` | `bigint` | Weight of stake supporting the ACP | | `supporters` | `Set` | Set of node IDs supporting the ACP | | `objectWeight` | `bigint` | Weight of stake objecting to the ACP | | `objectors` | `Set` | Set of node IDs objecting to the ACP | | `abstainWeight` | `bigint` | Weight of stake abstaining from the ACP | **Example:** ```typescript const acps = await client.info.acps(); console.log("ACP preferences:", acps.acps); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/info-rpc#infoacps) --- ## Health API Client Provides health monitoring for the node. **Access:** `client.health` ### health Get health check results for the node. **Function Signature:** ```typescript function health(params: HealthParameters): Promise; interface HealthParameters { tags?: string[]; } interface HealthReturnType { healthy: boolean; checks: { C: ChainHealthCheck; P: ChainHealthCheck; X: ChainHealthCheck; bootstrapped: { message: any[]; timestamp: string; duration: number; }; database: { timestamp: string; duration: number; }; diskspace: { message: { availableDiskBytes: number; }; timestamp: string; duration: number; }; network: { message: { connectedPeers: number; sendFailRate: number; timeSinceLastMsgReceived: string; timeSinceLastMsgSent: string; }; timestamp: string; duration: number; }; router: { message: { longestRunningRequest: string; outstandingRequests: number; }; timestamp: string; duration: number; }; }; } type ChainHealthCheck = { message: { engine: { consensus: { lastAcceptedHeight: number; lastAcceptedID: string; longestProcessingBlock: string; processingBlocks: number; }; vm: null; }; networking: { percentConnected: number; }; }; timestamp: string; duration: number; }; ``` **Parameters:** | Name | Type | Required | Description | | ------ | ---------- | -------- | ------------------------------------- | | `tags` | `string[]` | No | Optional tags to filter health checks | **Returns:** | Type | Description | | ------------------ | -------------------- | | `HealthReturnType` | Health check results | **Return Object:** | Property | Type | Description | | --------- | --------- | --------------------------------------- | | `healthy` | `boolean` | Overall health status of the node | | `checks` | `object` | Health check results for each component | **Example:** ```typescript const healthStatus = await client.health.health({ tags: ["11111111111111111111111111111111LpoYY"], }); console.log("Node healthy:", healthStatus.healthy); console.log("C-Chain health:", healthStatus.checks.C); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/health-rpc#healthhealth) --- ### liveness Get liveness check indicating if the node is alive and can handle requests. **Function Signature:** ```typescript function liveness(): Promise; interface LivenessReturnType { checks: object; healthy: boolean; } ``` **Returns:** | Type | Description | | -------------------- | --------------------- | | `LivenessReturnType` | Liveness check result | **Return Object:** | Property | Type | Description | | --------- | --------- | ------------------------------------------------------ | | `checks` | `object` | Liveness check details | | `healthy` | `boolean` | Indicates if the node is alive and can handle requests | **Example:** ```typescript const livenessStatus = await client.health.liveness(); if (livenessStatus.healthy) { console.log("Node is alive and responding"); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/health-rpc#healthliveness) --- ### readiness Get readiness check indicating if the node has finished initializing. **Function Signature:** ```typescript function readiness(params: ReadinessParameters): Promise; interface ReadinessParameters { tags?: string[]; } interface ReadinessReturnType { checks: { [key: string]: { message: { timestamp: string; duration: number; contiguousFailures: number; timeOfFirstFailure: string | null; }; healthy: boolean; }; }; healthy: boolean; } ``` **Parameters:** | Name | Type | Required | Description | | ------ | ---------- | -------- | ---------------------------------------- | | `tags` | `string[]` | No | Optional tags to filter readiness checks | **Returns:** | Type | Description | | --------------------- | ---------------------- | | `ReadinessReturnType` | Readiness check result | **Return Object:** | Property | Type | Description | | --------- | --------- | ------------------------------------------ | | `checks` | `object` | Readiness check results for each component | | `healthy` | `boolean` | Overall readiness status of the node | **Example:** ```typescript const readinessStatus = await client.health.readiness({ tags: ["11111111111111111111111111111111LpoYY"], }); if (readinessStatus.healthy) { console.log("Node is ready to handle requests"); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/health-rpc#healthreadiness) --- ## Index API Clients Provides indexed blockchain data queries for fast container lookups. **Access:** - `client.indexBlock.pChain` - P-Chain block index - `client.indexBlock.cChain` - C-Chain block index - `client.indexBlock.xChain` - X-Chain block index - `client.indexTx.xChain` - X-Chain transaction index ### getContainerByIndex Get container by its index. **Function Signature:** ```typescript function getContainerByIndex( params: GetContainerByIndexParameters ): Promise; interface GetContainerByIndexParameters { index: number; encoding: "hex"; } interface GetContainerByIndexReturnType { id: string; bytes: string; timestamp: string; encoding: "hex"; index: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ----------------------------------------------- | | `index` | `number` | Yes | Container index (first container is at index 0) | | `encoding` | `"hex"` | Yes | Encoding format (only "hex" is supported) | **Returns:** | Type | Description | | ------------------------------- | ---------------- | | `GetContainerByIndexReturnType` | Container object | **Return Object:** | Property | Type | Description | | ----------- | -------- | ------------------------------------------------- | | `id` | `string` | Container's ID | | `bytes` | `string` | Byte representation of the container | | `timestamp` | `string` | Time at which this node accepted the container | | `encoding` | `"hex"` | Encoding format used | | `index` | `string` | How many containers were accepted before this one | **Example:** ```typescript const container = await client.indexBlock.pChain.getContainerByIndex({ index: 12345, encoding: "hex", }); console.log("Container ID:", container.id); console.log("Container bytes:", container.bytes); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/index-rpc#indexgetcontainerbyindex) --- ### getContainerByID Get container by its ID. **Function Signature:** ```typescript function getContainerByID( params: GetContainerByIDParameters ): Promise; interface GetContainerByIDParameters { id: string; encoding: "hex"; } interface GetContainerByIDReturnType { id: string; bytes: string; timestamp: string; encoding: "hex"; index: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ----------------------------------------- | | `id` | `string` | Yes | Container's ID | | `encoding` | `"hex"` | Yes | Encoding format (only "hex" is supported) | **Returns:** | Type | Description | | ---------------------------- | ---------------- | | `GetContainerByIDReturnType` | Container object | **Return Object:** | Property | Type | Description | | ----------- | -------- | ------------------------------------------------- | | `id` | `string` | Container's ID | | `bytes` | `string` | Byte representation of the container | | `timestamp` | `string` | Time at which this node accepted the container | | `encoding` | `"hex"` | Encoding format used | | `index` | `string` | How many containers were accepted before this one | **Example:** ```typescript const container = await client.indexBlock.cChain.getContainerByID({ id: "0x123...", encoding: "hex", }); console.log("Container index:", container.index); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/index-rpc#indexgetcontainerbyid) --- ### getContainerRange Get range of containers by index. **Function Signature:** ```typescript function getContainerRange( params: GetContainerRangeParameters ): Promise; interface GetContainerRangeParameters { startIndex: number; endIndex: number; encoding: "hex"; } interface GetContainerRangeReturnType { containers: Array<{ id: string; bytes: string; timestamp: string; encoding: "hex"; index: string; }>; } ``` **Parameters:** | Name | Type | Required | Description | | ------------ | -------- | -------- | --------------------------------------------------- | | `startIndex` | `number` | Yes | Index of the first container to retrieve | | `endIndex` | `number` | Yes | Index of the last container to retrieve (inclusive) | | `encoding` | `"hex"` | Yes | Encoding format (only "hex" is supported) | **Returns:** | Type | Description | | ----------------------------- | ---------------------- | | `GetContainerRangeReturnType` | Container range object | **Return Object:** | Property | Type | Description | | ------------ | ------- | -------------------------- | | `containers` | `array` | Array of container details | **Container Object:** | Property | Type | Description | | ----------- | -------- | ------------------------------------------------- | | `id` | `string` | Container's ID | | `bytes` | `string` | Byte representation of the container | | `timestamp` | `string` | Time at which this node accepted the container | | `encoding` | `"hex"` | Encoding format used | | `index` | `string` | How many containers were accepted before this one | **Example:** ```typescript const range = await client.indexBlock.xChain.getContainerRange({ startIndex: 1000, endIndex: 1010, encoding: "hex", }); console.log("Containers:", range.containers.length); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/index-rpc#indexgetcontainerrange) --- ### getIndex Get container index by ID. **Function Signature:** ```typescript function getIndex(params: GetIndexParameters): Promise; interface GetIndexParameters { id: string; encoding: "hex"; } interface GetIndexReturnType { index: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ----------------------------------------- | | `id` | `string` | Yes | Container's ID | | `encoding` | `"hex"` | Yes | Encoding format (only "hex" is supported) | **Returns:** | Type | Description | | -------------------- | ------------ | | `GetIndexReturnType` | Index object | **Return Object:** | Property | Type | Description | | -------- | -------- | ------------------------------------------------------ | | `index` | `string` | Index of the container (first container is at index 0) | **Example:** ```typescript const result = await client.indexTx.xChain.getIndex({ id: "0x123...", encoding: "hex", }); console.log("Container index:", result.index); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/index-rpc#indexgetindex) --- ### getLastAccepted Get last accepted container. **Function Signature:** ```typescript function getLastAccepted( params: GetLastAcceptedParameters ): Promise; interface GetLastAcceptedParameters { encoding: "hex"; } interface GetLastAcceptedReturnType { id: string; bytes: string; timestamp: string; encoding: "hex"; index: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ------- | -------- | ----------------------------------------- | | `encoding` | `"hex"` | Yes | Encoding format (only "hex" is supported) | **Returns:** | Type | Description | | --------------------------- | ------------------------------ | | `GetLastAcceptedReturnType` | Last accepted container object | **Return Object:** | Property | Type | Description | | ----------- | -------- | ------------------------------------------------- | | `id` | `string` | Container's ID | | `bytes` | `string` | Byte representation of the container | | `timestamp` | `string` | Time at which this node accepted the container | | `encoding` | `"hex"` | Encoding format used | | `index` | `string` | How many containers were accepted before this one | **Example:** ```typescript const lastAccepted = await client.indexBlock.pChain.getLastAccepted({ encoding: "hex", }); console.log("Last accepted container ID:", lastAccepted.id); console.log("Last accepted index:", lastAccepted.index); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/index-rpc#indexgetlastaccepted) --- ### isAccepted Check if container is accepted in the index. **Function Signature:** ```typescript function isAccepted( params: IsAcceptedParameters ): Promise; interface IsAcceptedParameters { id: string; encoding: "hex"; } interface IsAcceptedReturnType { isAccepted: boolean; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ----------------------------------------- | | `id` | `string` | Yes | Container's ID | | `encoding` | `"hex"` | Yes | Encoding format (only "hex" is supported) | **Returns:** | Type | Description | | ---------------------- | ------------------------ | | `IsAcceptedReturnType` | Acceptance status object | **Return Object:** | Property | Type | Description | | ------------ | --------- | -------------------------------------- | | `isAccepted` | `boolean` | Whether the container is in this index | **Example:** ```typescript const result = await client.indexBlock.cChain.isAccepted({ id: "0x123...", encoding: "hex", }); if (result.isAccepted) { console.log("Container is accepted"); } else { console.log("Container is not accepted"); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/other/index-rpc#indexisaccepted) --- ## ProposerVM API Client Provides ProposerVM operations for each chain. ProposerVM is responsible for proposing blocks and managing consensus. **Access:** - `client.proposerVM.pChain` - P-Chain ProposerVM - `client.proposerVM.xChain` - X-Chain ProposerVM - `client.proposerVM.cChain` - C-Chain ProposerVM ### getProposedHeight Get the current proposed height for the chain. **Function Signature:** ```typescript function getProposedHeight(): Promise; interface GetProposedHeightReturnType { height: string; } ``` **Returns:** | Type | Description | | ----------------------------- | ---------------------- | | `GetProposedHeightReturnType` | Proposed height object | **Return Object:** | Property | Type | Description | | -------- | -------- | -------------------------------------- | | `height` | `string` | This node's current proposer VM height | **Example:** ```typescript const pChainHeight = await client.proposerVM.pChain.getProposedHeight(); console.log("P-Chain proposed height:", pChainHeight.height); const xChainHeight = await client.proposerVM.xChain.getProposedHeight(); console.log("X-Chain proposed height:", xChainHeight.height); const cChainHeight = await client.proposerVM.cChain.getProposedHeight(); console.log("C-Chain proposed height:", cChainHeight.height); ``` **Related:** - [API Reference](https://build.avax.network/docs/api-reference/proposervm-api#proposervmgetproposedheight) --- ### getCurrentEpoch Get the current epoch information. **Function Signature:** ```typescript function getCurrentEpoch(): Promise; interface GetCurrentEpochReturnType { number: string; startTime: string; pChainHeight: string; } ``` **Returns:** | Type | Description | | --------------------------- | -------------------- | | `GetCurrentEpochReturnType` | Current epoch object | **Return Object:** | Property | Type | Description | | -------------- | -------- | --------------------------------------------- | | `number` | `string` | The current epoch number | | `startTime` | `string` | The epoch start time (Unix timestamp) | | `pChainHeight` | `string` | The P-Chain height at the start of this epoch | **Example:** ```typescript const epoch = await client.proposerVM.pChain.getCurrentEpoch(); console.log("Current epoch:", epoch.number); console.log("Epoch start time:", epoch.startTime); console.log("P-Chain height at epoch start:", epoch.pChainHeight); ``` **Related:** - [API Reference](https://build.avax.network/docs/api-reference/proposervm-api#proposervmgetcurrentepoch) --- ## Next Steps - **[API Clients Documentation](clients/api-clients)** - Detailed API client reference - **[Main Clients](clients)** - Client architecture overview - **[Getting Started](getting-started)** - Quick start guide # C-Chain Methods (/docs/tooling/avalanche-sdk/client/methods/public-methods/c-chain) --- title: C-Chain Methods description: Complete reference for C-Chain (Contract Chain) methods and EVM compatibility --- ## Overview The C-Chain (Contract Chain) is Avalanche's instance of the Ethereum Virtual Machine (EVM), providing full Ethereum compatibility with additional Avalanche-specific features like cross-chain atomic transactions and UTXO management. **Note:** The Avalanche Client SDK fully extends [viem](https://viem.sh), meaning all standard EVM methods are also available. See the [viem documentation](https://viem.sh/docs) for complete EVM method reference. ## Atomic Transaction Operations ### getAtomicTx Get an atomic transaction by its ID. Atomic transactions enable cross-chain transfers between the C-Chain and other Avalanche chains (P-Chain, X-Chain). **Function Signature:** ```typescript function getAtomicTx( params: GetAtomicTxParameters ): Promise; interface GetAtomicTxParameters { txID: string; encoding?: "hex"; } interface GetAtomicTxReturnType { tx: string; blockHeight: string; encoding: "hex"; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ------------------------------------------------------- | | `txID` | `string` | Yes | Transaction ID in CB58 format | | `encoding` | `"hex"` | No | Encoding format for the transaction (defaults to "hex") | **Returns:** | Type | Description | | ----------------------- | ------------------------- | | `GetAtomicTxReturnType` | Atomic transaction object | **Return Object:** | Property | Type | Description | | ------------- | -------- | ---------------------------------------------- | | `tx` | `string` | Transaction bytes in hex format | | `blockHeight` | `string` | Height of the block containing the transaction | | `encoding` | `"hex"` | Encoding format used | **Example:** ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const atomicTx = await client.cChain.getAtomicTx({ txID: "2QouvMUbQ6oy7yQ9tLvL3L8tGQG2QK1wJ1q1wJ1q1wJ1q1wJ1q1wJ1q1wJ1", }); console.log("Transaction:", atomicTx.tx); console.log("Block height:", atomicTx.blockHeight); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#avaxgetatomictx) - [getAtomicTxStatus](#getatomictxstatus) - Get transaction status --- ### getAtomicTxStatus Get the status of an atomic transaction. Returns the current processing state and block information. **Function Signature:** ```typescript function getAtomicTxStatus( params: GetAtomicTxStatusParameters ): Promise; interface GetAtomicTxStatusParameters { txID: string; } interface GetAtomicTxStatusReturnType { status: CChainAtomicTxStatus; blockHeight: string; } type CChainAtomicTxStatus = "Accepted" | "Processing" | "Dropped" | "Unknown"; ``` **Parameters:** | Name | Type | Required | Description | | ------ | -------- | -------- | ----------------------------- | | `txID` | `string` | Yes | Transaction ID in CB58 format | **Returns:** | Type | Description | | ----------------------------- | ------------------------- | | `GetAtomicTxStatusReturnType` | Transaction status object | **Return Object:** | Property | Type | Description | | ------------- | ---------------------- | --------------------------------------------------------------------- | | `status` | `CChainAtomicTxStatus` | Transaction status: "Accepted", "Processing", "Dropped", or "Unknown" | | `blockHeight` | `string` | Height of the block containing the transaction (if accepted) | **Status Values:** - **Accepted**: Transaction is (or will be) accepted by every node - **Processing**: Transaction is being voted on by this node - **Dropped**: Transaction was dropped by this node because it thought the transaction invalid - **Unknown**: Transaction hasn't been seen by this node **Example:** ```typescript const status = await client.cChain.getAtomicTxStatus({ txID: "2QouvMUbQ6oy7yQ9tLvL3L8tGQG2QK1wJ1q1wJ1q1wJ1q1wJ1q1wJ1q1wJ1", }); console.log("Status:", status.status); if (status.status === "Accepted") { console.log("Block height:", status.blockHeight); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#avaxgetatomictxstatus) - [getAtomicTx](#getatomictx) - Get transaction details --- ## UTXO Operations ### getUTXOs Get the UTXOs (Unspent Transaction Outputs) for a set of addresses. UTXOs represent unspent native AVAX on the C-Chain from imported transactions. **Function Signature:** ```typescript function getUTXOs(params: GetUTXOsParameters): Promise; interface GetUTXOsParameters { addresses: string[]; limit?: number; startIndex?: { address: string; utxo: string; }; sourceChain?: string; encoding?: "hex"; } interface GetUTXOsReturnType { numFetched: number; utxos: string[]; endIndex: { address: string; utxo: string; }; } ``` **Parameters:** | Name | Type | Required | Description | | ------------- | ----------------------------------- | -------- | -------------------------------------------- | | `addresses` | `string[]` | Yes | Array of C-Chain addresses | | `limit` | `number` | No | Maximum number of UTXOs to return (max 1024) | | `startIndex` | `{ address: string; utxo: string }` | No | Pagination cursor for next page | | `sourceChain` | `string` | No | Source chain ID for filtering UTXOs | | `encoding` | `"hex"` | No | Encoding format for returned UTXOs | **Returns:** | Type | Description | | -------------------- | ------------------------- | | `GetUTXOsReturnType` | UTXO data with pagination | **Return Object:** | Property | Type | Description | | ------------ | ----------------------------------- | ---------------------------------------- | | `numFetched` | `number` | Number of UTXOs fetched in this response | | `utxos` | `string[]` | Array of UTXO bytes (hex encoded) | | `endIndex` | `{ address: string; utxo: string }` | Pagination cursor for fetching next page | **Example:** ```typescript // Get UTXOs from X-Chain const utxos = await client.cChain.getUTXOs({ addresses: ["0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6"], limit: 100, sourceChain: "X", }); console.log("Number of UTXOs:", utxos.numFetched); console.log("UTXOs:", utxos.utxos); // Get next page if needed if (utxos.endIndex) { const moreUTXOs = await client.cChain.getUTXOs({ addresses: ["0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6"], startIndex: utxos.endIndex, limit: 100, }); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#avaxgetutxos) - [C-Chain Wallet Methods](../wallet-methods/c-chain-wallet) - Atomic transaction operations --- ## Transaction Operations ### issueTx Issue a transaction to the C-Chain. Submits a signed transaction for processing. **Function Signature:** ```typescript function issueTx(params: IssueTxParameters): Promise; interface IssueTxParameters { tx: string; encoding: "hex"; } interface IssueTxReturnType { txID: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ------------------------------- | | `tx` | `string` | Yes | Transaction bytes in hex format | | `encoding` | `"hex"` | Yes | Encoding format (must be "hex") | **Returns:** | Type | Description | | ------------------- | --------------------- | | `IssueTxReturnType` | Transaction ID object | **Return Object:** | Property | Type | Description | | -------- | -------- | ----------------------------- | | `txID` | `string` | Transaction ID in CB58 format | **Example:** ```typescript const txID = await client.cChain.issueTx({ tx: "0x00000009de31b4d8b22991d51aa6aa1fc733f23a851a8c9400000000000186a0...", encoding: "hex", }); console.log("Transaction ID:", txID.txID); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#avaxissuetx) --- ## Admin Operations These methods are available for node administration and debugging. They require admin access to the node. ### setLogLevel Set the log level for the C-Chain node. **Function Signature:** ```typescript function setLogLevel(params: SetLogLevelParameters): Promise; interface SetLogLevelParameters { level: string; } ``` **Parameters:** | Name | Type | Required | Description | | ------- | -------- | -------- | -------------------------------------------------- | | `level` | `string` | Yes | Log level (e.g., "debug", "info", "warn", "error") | **Returns:** | Type | Description | | ------ | -------------------------------------- | | `void` | Promise resolves when log level is set | **Example:** ```typescript await client.cChain.setLogLevel({ level: "info", }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#adminsetloglevel) --- ### startCPUProfiler Start the CPU profiler for performance analysis. **Function Signature:** ```typescript function startCPUProfiler(): Promise; ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ------ | ----------------------------------------- | | `void` | Promise resolves when profiler is started | **Example:** ```typescript await client.cChain.startCPUProfiler(); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#adminstartcpuprofiler) - [stopCPUProfiler](#stopcpuprofiler) - Stop the CPU profiler --- ### stopCPUProfiler Stop the CPU profiler. **Function Signature:** ```typescript function stopCPUProfiler(): Promise; ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ------ | ----------------------------------------- | | `void` | Promise resolves when profiler is stopped | **Example:** ```typescript await client.cChain.stopCPUProfiler(); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#adminstopcpuprofiler) - [startCPUProfiler](#startcpuprofiler) - Start the CPU profiler --- ### memoryProfile Get the memory profile of the C-Chain node. **Function Signature:** ```typescript function memoryProfile(): Promise; ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ------ | ------------------------------------------------- | | `void` | Promise resolves when memory profile is retrieved | **Example:** ```typescript await client.cChain.memoryProfile(); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#adminmemoryprofile) --- ### lockProfile Lock the profile to prevent modifications. **Function Signature:** ```typescript function lockProfile(): Promise; ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ------ | --------------------------------------- | | `void` | Promise resolves when profile is locked | **Example:** ```typescript await client.cChain.lockProfile(); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#adminlockprofile) --- ## Standard EVM Methods The C-Chain client extends viem's Public Client, providing access to all standard Ethereum methods. Here are some commonly used methods: ### Block Operations ```typescript // Get block number const blockNumber = await client.getBlockNumber(); // Get block by number const block = await client.getBlock({ blockNumber: 12345n, }); // Get block by hash const blockByHash = await client.getBlock({ blockHash: "0x...", }); ``` ### Balance Operations ```typescript // Get balance const balance = await client.getBalance({ address: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", }); // Get balance with block number const balanceAtBlock = await client.getBalance({ address: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", blockNumber: 12345n, }); ``` ### Transaction Operations ```typescript // Get transaction const tx = await client.getTransaction({ hash: "0x...", }); // Get transaction receipt const receipt = await client.getTransactionReceipt({ hash: "0x...", }); // Get transaction count (nonce) const nonce = await client.getTransactionCount({ address: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", }); ``` ### Gas Operations ```typescript // Get gas price const gasPrice = await client.getGasPrice(); // Get max priority fee per gas const maxPriorityFee = await client.maxPriorityFeePerGas(); // Estimate gas const estimatedGas = await client.estimateGas({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", value: parseEther("0.001"), }); ``` ### Contract Operations ```typescript // Read contract const result = await client.readContract({ address: "0x...", abi: [...], functionName: "balanceOf", args: [address], }); // Simulate contract const { request } = await client.simulateContract({ address: "0x...", abi: [...], functionName: "transfer", args: [to, amount], }); ``` **For complete EVM method reference, see:** [Viem Documentation](https://viem.sh/docs) --- ## Next Steps - **[C-Chain Wallet Methods](../wallet-methods/c-chain-wallet)** - Atomic transaction operations - **[Wallet Client](../../clients/wallet-client)** - Complete wallet operations - **[Account Management](../../accounts)** - Account types and management - **[Viem Documentation](https://viem.sh/docs)** - Complete EVM method reference # P-Chain Methods (/docs/tooling/avalanche-sdk/client/methods/public-methods/p-chain) --- title: P-Chain Methods description: Complete reference for P-Chain (Platform Chain) methods --- ## Overview The P-Chain (Platform Chain) is Avalanche's coordinating chain responsible for managing validators, delegators, subnets, and blockchains. This reference covers all read-only P-Chain operations available through the Avalanche Client SDK. ## Balance Operations ### getBalance Get the balance of AVAX controlled by a given address. **Function Signature:** ```typescript function getBalance( params: GetBalanceParameters ): Promise; interface GetBalanceParameters { addresses: string[]; } interface GetBalanceReturnType { balance: bigint; unlocked: bigint; lockedStakeable: bigint; lockedNotStakeable: bigint; utxoIDs: { txID: string; outputIndex: number; }[]; } ``` **Parameters:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ----------------------------------- | | `addresses` | `string[]` | Yes | Array of P-Chain addresses to query | **Returns:** | Type | Description | | ---------------------- | -------------------------- | | `GetBalanceReturnType` | Balance information object | **Return Object:** | Property | Type | Description | | -------------------- | -------- | ------------------------------------------- | | `balance` | `bigint` | Total balance | | `unlocked` | `bigint` | Unlocked balance | | `lockedStakeable` | `bigint` | Locked and stakeable balance | | `lockedNotStakeable` | `bigint` | Locked but not stakeable balance | | `utxoIDs` | `array` | Array of UTXO IDs referencing the addresses | **Example:** ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const balance = await client.pChain.getBalance({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], }); console.log("Total balance:", balance.balance); console.log("Unlocked:", balance.unlocked); console.log("Locked stakeable:", balance.lockedStakeable); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetbalance) - [getUTXOs](#getutxos) - Get UTXOs for addresses --- ### getUTXOs Get the UTXOs (Unspent Transaction Outputs) controlled by a set of addresses. **Function Signature:** ```typescript function getUTXOs(params: GetUTXOsParameters): Promise; interface GetUTXOsParameters { addresses: string[]; sourceChain?: string; limit?: number; startIndex?: { address: string; utxo: string; }; encoding?: "hex"; } interface GetUTXOsReturnType { numFetched: number; utxos: string[]; endIndex: { address: string; utxo: string; }; sourceChain?: string; encoding: "hex"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------- | ----------------------------------- | -------- | ----------------------------------------------- | | `addresses` | `string[]` | Yes | Array of P-Chain addresses | | `sourceChain` | `string` | No | Source chain ID (e.g., "X" for X-Chain) | | `limit` | `number` | No | Maximum number of UTXOs to return | | `startIndex` | `{ address: string; utxo: string }` | No | Pagination cursor for next page | | `encoding` | `"hex"` | No | Encoding format (can only be "hex" if provided) | **Returns:** | Type | Description | | -------------------- | ------------------------- | | `GetUTXOsReturnType` | UTXO data with pagination | **Return Object:** | Property | Type | Description | | ------------- | ----------------------------------- | ---------------------------------------- | | `numFetched` | `number` | Number of UTXOs fetched in this response | | `utxos` | `string[]` | Array of UTXO bytes (hex encoded) | | `endIndex` | `{ address: string; utxo: string }` | Pagination cursor for fetching next page | | `sourceChain` | `string` | Source chain ID (if specified) | | `encoding` | `"hex"` | Encoding format used | **Example:** ```typescript // Get first page const utxos = await client.pChain.getUTXOs({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], limit: 100, }); console.log("Fetched UTXOs:", utxos.numFetched); console.log("UTXOs:", utxos.utxos); // Get next page if needed if (utxos.endIndex) { const moreUTXOs = await client.pChain.getUTXOs({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], startIndex: utxos.endIndex, limit: 100, }); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetutxos) - [getBalance](#getbalance) - Get balance summary --- ## Validator Operations ### getCurrentValidators Get the current validators of the specified Subnet. **Function Signature:** ```typescript function getCurrentValidators( params: GetCurrentValidatorsParameters ): Promise; interface GetCurrentValidatorsParameters { subnetID?: string | Buffer; nodeIDs?: string[]; } interface GetCurrentValidatorsReturnType { validators: Array<{ accruedDelegateeReward: string; txID: string; startTime: string; endTime?: string; stakeAmount: string; nodeID: string; weight: string; validationRewardOwner?: { locktime: string; threshold: string; addresses: string[]; }; delegationRewardOwner?: { locktime: string; threshold: string; addresses: string[]; }; signer?: { publicKey: string; proofOfPosession: string; }; delegatorCount?: string; delegatorWeight?: string; potentialReward?: string; delegationFee?: string; uptime?: string; connected?: boolean; delegators?: Array<{ txID: string; startTime: string; endTime: string; stakeAmount: string; nodeID: string; rewardOwner: { locktime: string; threshold: string; addresses: string[]; }; potentialReward: string; }>; }>; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ------------------ | -------- | --------------------------------------- | | `subnetID` | `string \| Buffer` | No | Subnet ID (defaults to Primary Network) | | `nodeIDs` | `string[]` | No | Specific NodeIDs to query | **Returns:** | Type | Description | | -------------------------------- | ---------------------- | | `GetCurrentValidatorsReturnType` | Validators list object | **Return Object:** | Property | Type | Description | | ------------ | ------- | ------------------------------------------- | | `validators` | `array` | List of validators for the specified Subnet | **Note:** Many fields in the validator object are omitted if `subnetID` is not the Primary Network. The `delegators` field is only included when `nodeIDs` specifies a single NodeID. **Example:** ```typescript // Get all validators on Primary Network const validators = await client.pChain.getCurrentValidators({}); console.log("Total validators:", validators.validators.length); // Get validators for specific subnet const subnetValidators = await client.pChain.getCurrentValidators({ subnetID: "11111111111111111111111111111111LpoYY", }); // Get specific validators const specificValidators = await client.pChain.getCurrentValidators({ subnetID: "11111111111111111111111111111111LpoYY", nodeIDs: ["NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"], }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetcurrentvalidators) - [getValidatorsAt](#getvalidatorsat) - Get validators at specific height - [getAllValidatorsAt](#getallvalidatorsat) - Get all validators at height - [sampleValidators](#samplevalidators) - Sample validators --- ### getValidatorsAt Get the validators at a specific height. **Function Signature:** ```typescript function getValidatorsAt( params: GetValidatorsAtParameters ): Promise; interface GetValidatorsAtParameters { height: number; subnetID?: string; } interface GetValidatorsAtReturnType { validators: Record; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | --------------------------------------- | | `height` | `number` | Yes | Block height to query | | `subnetID` | `string` | No | Subnet ID (defaults to Primary Network) | **Returns:** | Type | Description | | --------------------------- | --------------------- | | `GetValidatorsAtReturnType` | Validators map object | **Return Object:** | Property | Type | Description | | ------------ | ------------------------ | ------------------------------------------- | | `validators` | `Record` | Map of validator IDs to their stake amounts | **Example:** ```typescript const validators = await client.pChain.getValidatorsAt({ height: 1000001, subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Validators at height:", validators.validators); ``` **Links:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetvalidatorsat) - [Related: getCurrentValidators](#getcurrentvalidators) - Get current validators - [Related: getAllValidatorsAt](#getallvalidatorsat) - Get all validators at height - [Related: getHeight](#getheight) - Get current height --- ### getAllValidatorsAt Get all validators at a specific height across all Subnets and the Primary Network. **Function Signature:** ```typescript function getAllValidatorsAt( params: GetAllValidatorsAtParameters ): Promise; interface GetAllValidatorsAtParameters { height: number | "proposed"; } interface GetAllValidatorsAtReturnType { validatorSets: Record< string, { validators: Array<{ publicKey: string; weight: string; nodeIDs: string[]; }>; totalWeight: string; } >; } ``` **Parameters:** | Name | Type | Required | Description | | -------- | ---------------------- | -------- | -------------------------------------------------- | | `height` | `number \| "proposed"` | Yes | P-Chain height or "proposed" for proposervm height | **Returns:** | Type | Description | | ------------------------------ | --------------------- | | `GetAllValidatorsAtReturnType` | Validator sets object | **Return Object:** | Property | Type | Description | | --------------- | -------- | ------------------------------------------------ | | `validatorSets` | `object` | Map of Subnet IDs to their validator information | **Note:** The public API (api.avax.network) only supports height within 1000 blocks from the P-Chain tip. **Example:** ```typescript // Get all validators at specific height const validators = await client.pChain.getAllValidatorsAt({ height: 1000001, }); // Get validators at proposed height const proposedValidators = await client.pChain.getAllValidatorsAt({ height: "proposed", }); console.log("Subnet IDs:", Object.keys(validators.validatorSets)); Object.entries(validators.validatorSets).forEach(([subnetID, set]) => { console.log(`Subnet ${subnetID}:`); console.log(` Total weight: ${set.totalWeight}`); console.log(` Validators: ${set.validators.length}`); }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetallvalidatorsat) - [getCurrentValidators](#getcurrentvalidators) - Get current validators - [getValidatorsAt](#getvalidatorsat) - Get validators at height for specific subnet --- ### sampleValidators Sample validators from the specified Subnet. **Function Signature:** ```typescript function sampleValidators( params: SampleValidatorsParameters ): Promise; interface SampleValidatorsParameters { samplingSize: number; subnetID?: string; pChainHeight?: number; } interface SampleValidatorsReturnType { validators: string[]; } ``` **Parameters:** | Name | Type | Required | Description | | -------------- | -------- | -------- | --------------------------------------- | | `samplingSize` | `number` | Yes | Number of validators to sample | | `subnetID` | `string` | No | Subnet ID (defaults to Primary Network) | | `pChainHeight` | `number` | No | Block height (defaults to current) | **Returns:** | Type | Description | | ---------------------------- | ------------------------- | | `SampleValidatorsReturnType` | Sampled validators object | **Return Object:** | Property | Type | Description | | ------------ | ---------- | ---------------------------------- | | `validators` | `string[]` | Array of sampled validator NodeIDs | **Example:** ```typescript const sampled = await client.pChain.sampleValidators({ samplingSize: 5, subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Sampled validators:", sampled.validators); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformsamplevalidators) - [getCurrentValidators](#getcurrentvalidators) - Get all current validators --- ## Block Operations ### getHeight Get the height of the last accepted block. **Function Signature:** ```typescript function getHeight(): Promise; interface GetHeightReturnType { height: number; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | --------------------- | ------------- | | `GetHeightReturnType` | Height object | **Return Object:** | Property | Type | Description | | -------- | -------- | ---------------------------- | | `height` | `number` | Current P-Chain block height | **Example:** ```typescript const height = await client.pChain.getHeight(); console.log("Current P-Chain height:", height.height); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetheight) - [getBlockByHeight](#getblockbyheight) - Get block by height - [getProposedHeight](#getproposedheight) - Get proposed height --- ### getBlockByHeight Get a block by its height. **Function Signature:** ```typescript function getBlockByHeight( params: GetBlockByHeightParameters ): Promise; interface GetBlockByHeightParameters { height: number; encoding?: "hex" | "json"; } interface GetBlockByHeightReturnType { encoding: "hex" | "json"; block: string | object; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ----------------- | -------- | ----------------------------------- | | `height` | `number` | Yes | Block height | | `encoding` | `"hex" \| "json"` | No | Encoding format (defaults to "hex") | **Returns:** | Type | Description | | ---------------------------- | ----------------- | | `GetBlockByHeightReturnType` | Block data object | **Return Object:** | Property | Type | Description | | ---------- | ------------------ | ------------------------------------------- | | `encoding` | `"hex" \| "json"` | Encoding format used | | `block` | `string \| object` | Block data in the specified encoding format | **Example:** ```typescript const block = await client.pChain.getBlockByHeight({ height: 12345, encoding: "hex", }); console.log("Block data:", block.block); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetblockbyheight) - [getBlock](#getblock) - Get block by ID - [getHeight](#getheight) - Get current height --- ### getBlock Get a block by its ID. **Function Signature:** ```typescript function getBlock(params: GetBlockParameters): Promise; interface GetBlockParameters { blockId: string; encoding?: "hex" | "json"; } interface GetBlockReturnType { encoding: "hex" | "json"; block: string | object; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ----------------- | -------- | ----------------------------------- | | `blockId` | `string` | Yes | Block ID in CB58 format | | `encoding` | `"hex" \| "json"` | No | Encoding format (defaults to "hex") | **Returns:** | Type | Description | | -------------------- | ----------------- | | `GetBlockReturnType` | Block data object | **Return Object:** | Property | Type | Description | | ---------- | ------------------ | ------------------------------------------- | | `encoding` | `"hex" \| "json"` | Encoding format used | | `block` | `string \| object` | Block data in the specified encoding format | **Example:** ```typescript const block = await client.pChain.getBlock({ blockId: "d7WYmb8VeZNHsny3EJCwMm6QA37s1EHwMxw1Y71V3FqPZ5EFG", encoding: "hex", }); console.log("Block:", block.block); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetblock) - [getBlockByHeight](#getblockbyheight) - Get block by height --- ## Staking Operations ### getStake Get the stake amount for a set of addresses. **Function Signature:** ```typescript function getStake(params: GetStakeParameters): Promise; interface GetStakeParameters { addresses: string[]; subnetID: string; } interface GetStakeReturnType { stakeAmount: bigint; } ``` **Parameters:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ----------------- | | `addresses` | `string[]` | Yes | P-Chain addresses | | `subnetID` | `string` | Yes | Subnet ID | **Returns:** | Type | Description | | -------------------- | ------------------- | | `GetStakeReturnType` | Stake amount object | **Return Object:** | Property | Type | Description | | ------------- | -------- | ------------------------------------ | | `stakeAmount` | `bigint` | Total stake amount for the addresses | **Example:** ```typescript const stake = await client.pChain.getStake({ addresses: ["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Stake amount:", stake.stakeAmount); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetstake) - [getTotalStake](#gettotalstake) - Get total subnet stake - [getMinStake](#getminstake) - Get minimum stake requirements --- ### getTotalStake Get the total amount of stake for a Subnet. **Function Signature:** ```typescript function getTotalStake( params: GetTotalStakeParameters ): Promise; interface GetTotalStakeParameters { subnetID: string; } interface GetTotalStakeReturnType { stake: bigint; weight: bigint; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ----------- | | `subnetID` | `string` | Yes | Subnet ID | **Returns:** | Type | Description | | ------------------------- | ------------------ | | `GetTotalStakeReturnType` | Total stake object | **Return Object:** | Property | Type | Description | | -------- | -------- | --------------------------------- | | `stake` | `bigint` | Total stake amount for the subnet | | `weight` | `bigint` | Total weight for the subnet | **Example:** ```typescript const totalStake = await client.pChain.getTotalStake({ subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Total stake:", totalStake.stake); console.log("Total weight:", totalStake.weight); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgettotalstake) - [getStake](#getstake) - Get stake for addresses - [getMinStake](#getminstake) - Get minimum stake --- ### getMinStake Get the minimum stake required to validate or delegate. **Function Signature:** ```typescript function getMinStake( params: GetMinStakeParameters ): Promise; interface GetMinStakeParameters { subnetID: string; } interface GetMinStakeReturnType { minValidatorStake: bigint; minDelegatorStake: bigint; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ----------- | | `subnetID` | `string` | Yes | Subnet ID | **Returns:** | Type | Description | | ----------------------- | -------------------- | | `GetMinStakeReturnType` | Minimum stake object | **Return Object:** | Property | Type | Description | | ------------------- | -------- | -------------------------------------------- | | `minValidatorStake` | `bigint` | Minimum stake required to become a validator | | `minDelegatorStake` | `bigint` | Minimum stake required to delegate | **Example:** ```typescript const minStake = await client.pChain.getMinStake({ subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Min validator stake:", minStake.minValidatorStake); console.log("Min delegator stake:", minStake.minDelegatorStake); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetminstake) - [getTotalStake](#gettotalstake) - Get total subnet stake --- ## Subnet Operations ### getSubnet Get information about a subnet. **Function Signature:** ```typescript function getSubnet(params: GetSubnetParameters): Promise; interface GetSubnetParameters { subnetID: string; } interface GetSubnetReturnType { isPermissioned: boolean; controlKeys: string[]; threshold: string; locktime: string; subnetTransformationTxID: string; conversionID: string; managerChainID: string; managerAddress: string | null; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | -------------------- | | `subnetID` | `string` | Yes | The ID of the subnet | **Returns:** | Type | Description | | --------------------- | ------------------------- | | `GetSubnetReturnType` | Subnet information object | **Return Object:** | Property | Type | Description | | -------------------------- | ---------------- | ------------------------------------ | | `isPermissioned` | `boolean` | Whether the subnet is permissioned | | `controlKeys` | `string[]` | Control keys for the subnet | | `threshold` | `string` | Signature threshold | | `locktime` | `string` | Locktime for the subnet | | `subnetTransformationTxID` | `string` | Subnet transformation transaction ID | | `conversionID` | `string` | Conversion ID | | `managerChainID` | `string` | Manager chain ID | | `managerAddress` | `string \| null` | Manager address (null if not set) | **Example:** ```typescript const subnet = await client.pChain.getSubnet({ subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Is permissioned:", subnet.isPermissioned); console.log("Control keys:", subnet.controlKeys); console.log("Threshold:", subnet.threshold); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetsubnet) - [getSubnets](#getsubnets) - Get multiple subnets --- ### getSubnets Get information about multiple subnets. **Function Signature:** ```typescript function getSubnets( params: GetSubnetsParameters ): Promise; interface GetSubnetsParameters { ids: string[]; } interface GetSubnetsReturnType { subnets: { id: string; controlKeys: string[]; threshold: string; }[]; } ``` **Parameters:** | Name | Type | Required | Description | | ----- | ---------- | -------- | ---------------------------- | | `ids` | `string[]` | Yes | Array of subnet IDs to query | **Returns:** | Type | Description | | ---------------------- | -------------------------- | | `GetSubnetsReturnType` | Subnets information object | **Return Object:** | Property | Type | Description | | --------- | ------- | ----------------------------------- | | `subnets` | `array` | Array of subnet information objects | **Example:** ```typescript const subnets = await client.pChain.getSubnets({ ids: [ "11111111111111111111111111111111LpoYY", "SubnetID-11111111111111111111111111111111LpoYY", ], }); console.log("Number of subnets:", subnets.subnets.length); subnets.subnets.forEach((subnet) => { console.log("Subnet ID:", subnet.id); console.log("Control keys:", subnet.controlKeys); }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetsubnets) - [getSubnet](#getsubnet) - Get single subnet --- ### getStakingAssetID Get the staking asset ID for a subnet. **Function Signature:** ```typescript function getStakingAssetID( params: GetStakingAssetIDParameters ): Promise; interface GetStakingAssetIDParameters { subnetID: string; } interface GetStakingAssetIDReturnType { assetID: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | -------------------- | | `subnetID` | `string` | Yes | The ID of the subnet | **Returns:** | Type | Description | | ----------------------------- | ----------------------- | | `GetStakingAssetIDReturnType` | Staking asset ID object | **Return Object:** | Property | Type | Description | | --------- | -------- | --------------------------------------- | | `assetID` | `string` | Asset ID used for staking on the subnet | **Example:** ```typescript const stakingAsset = await client.pChain.getStakingAssetID({ subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Staking asset ID:", stakingAsset.assetID); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetstakingassetid) - [getSubnet](#getsubnet) - Get subnet information --- ## Blockchain Operations ### getBlockchains Get all the blockchains that exist (excluding the P-Chain). **Function Signature:** ```typescript function getBlockchains(): Promise; interface GetBlockchainsReturnType { blockchains: { id: string; name: string; subnetID: string; vmID: string; }[]; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | -------------------------- | ----------------------- | | `GetBlockchainsReturnType` | Blockchains list object | **Return Object:** | Property | Type | Description | | ------------- | ------- | --------------------------------------- | | `blockchains` | `array` | Array of blockchain information objects | **Example:** ```typescript const blockchains = await client.pChain.getBlockchains(); console.log("Number of blockchains:", blockchains.blockchains.length); blockchains.blockchains.forEach((blockchain) => { console.log("Blockchain:", blockchain.name); console.log(" ID:", blockchain.id); console.log(" Subnet ID:", blockchain.subnetID); console.log(" VM ID:", blockchain.vmID); }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetblockchains) - [getBlockchainStatus](#getblockchainstatus) - Get blockchain status --- ### getBlockchainStatus Get the status of a blockchain. **Function Signature:** ```typescript function getBlockchainStatus( params: GetBlockchainStatusParameters ): Promise; interface GetBlockchainStatusParameters { blockchainId: string; } interface GetBlockchainStatusReturnType { status: "Validating" | "Created" | "Preferred" | "Syncing" | "Unknown"; } ``` **Parameters:** | Name | Type | Required | Description | | -------------- | -------- | -------- | ------------------------ | | `blockchainId` | `string` | Yes | The ID of the blockchain | **Returns:** | Type | Description | | ------------------------------- | ------------------------ | | `GetBlockchainStatusReturnType` | Blockchain status object | **Return Object:** | Property | Type | Description | | -------- | -------- | -------------------------------------------------------------------------------- | | `status` | `string` | Blockchain status: "Validating", "Created", "Preferred", "Syncing", or "Unknown" | **Status Values:** - **Validating**: The blockchain is being validated by this node - **Created**: The blockchain exists but isn't being validated by this node - **Preferred**: The blockchain was proposed to be created and is likely to be created, but the transaction isn't yet accepted - **Syncing**: This node is participating in the blockchain as a non-validating node - **Unknown**: The blockchain either wasn't proposed or the proposal isn't preferred **Example:** ```typescript const status = await client.pChain.getBlockchainStatus({ blockchainId: "11111111111111111111111111111111LpoYY", }); console.log("Blockchain status:", status.status); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetblockchainstatus) - [getBlockchains](#getblockchains) - Get all blockchains --- ## Transaction Operations ### getTx Get a transaction by its ID. **Function Signature:** ```typescript function getTx(params: GetTxParameters): Promise; interface GetTxParameters { txID: string; encoding?: "hex" | "json"; } interface GetTxReturnType { encoding: "hex" | "json"; tx: string | object; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ----------------- | -------- | ----------------------------------- | | `txID` | `string` | Yes | Transaction ID in CB58 format | | `encoding` | `"hex" \| "json"` | No | Encoding format (defaults to "hex") | **Returns:** | Type | Description | | ----------------- | ----------------------- | | `GetTxReturnType` | Transaction data object | **Return Object:** | Property | Type | Description | | ---------- | ------------------ | ------------------------------------------------- | | `encoding` | `"hex" \| "json"` | Encoding format used | | `tx` | `string \| object` | Transaction data in the specified encoding format | **Example:** ```typescript const tx = await client.pChain.getTx({ txID: "11111111111111111111111111111111LpoYY", encoding: "hex", }); console.log("Transaction:", tx); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgettx) - [getTxStatus](#gettxstatus) - Get transaction status - [issueTx](#issuetx) - Issue a transaction --- ### getTxStatus Get the status of a transaction. **Function Signature:** ```typescript function getTxStatus( params: GetTxStatusParameters ): Promise; interface GetTxStatusParameters { txID: string; } interface GetTxStatusReturnType { status: "Committed" | "Pending" | "Dropped" | "Unknown"; reason?: string; } ``` **Parameters:** | Name | Type | Required | Description | | ------ | -------- | -------- | ----------------------------- | | `txID` | `string` | Yes | Transaction ID in CB58 format | **Returns:** | Type | Description | | ----------------------- | ------------------------- | | `GetTxStatusReturnType` | Transaction status object | **Return Object:** | Property | Type | Description | | -------- | -------- | ------------------------------------------------------------------- | | `status` | `string` | Transaction status: "Committed", "Pending", "Dropped", or "Unknown" | | `reason` | `string` | Optional reason for the status (if dropped) | **Status Values:** - **Committed**: The transaction is (or will be) accepted by every node - **Pending**: The transaction is being voted on by this node - **Dropped**: The transaction will never be accepted by any node in the network - **Unknown**: The transaction hasn't been seen by this node **Example:** ```typescript const txStatus = await client.pChain.getTxStatus({ txID: "11111111111111111111111111111111LpoYY", }); console.log("Transaction status:", txStatus.status); if (txStatus.reason) { console.log("Reason:", txStatus.reason); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgettxstatus) - [getTx](#gettx) - Get transaction details - [issueTx](#issuetx) - Issue a transaction --- ### issueTx Issue a transaction to the Platform Chain. **Function Signature:** ```typescript function issueTx(params: IssueTxParameters): Promise; interface IssueTxParameters { tx: string; encoding: "hex"; } interface IssueTxReturnType { txID: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ------------------------------- | | `tx` | `string` | Yes | Transaction bytes in hex format | | `encoding` | `"hex"` | Yes | Encoding format (must be "hex") | **Returns:** | Type | Description | | ------------------- | --------------------- | | `IssueTxReturnType` | Transaction ID object | **Return Object:** | Property | Type | Description | | -------- | -------- | ----------------------------- | | `txID` | `string` | Transaction ID in CB58 format | **Example:** ```typescript const txID = await client.pChain.issueTx({ tx: "0x00000009de31b4d8b22991d51aa6aa1fc733f23a851a8c9400000000000186a0...", encoding: "hex", }); console.log("Transaction issued:", txID.txID); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformissuetx) - [getTx](#gettx) - Get transaction details - [getTxStatus](#gettxstatus) - Get transaction status --- ## Block Operations ### getProposedHeight Get the proposed height of the P-Chain. **Function Signature:** ```typescript function getProposedHeight(): Promise; interface GetProposedHeightReturnType { height: number; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ----------------------------- | ---------------------- | | `GetProposedHeightReturnType` | Proposed height object | **Return Object:** | Property | Type | Description | | -------- | -------- | ----------------------------- | | `height` | `number` | Proposed P-Chain block height | **Example:** ```typescript const proposedHeight = await client.pChain.getProposedHeight(); console.log("Proposed height:", proposedHeight.height); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetproposedheight) - [getHeight](#getheight) - Get current accepted height --- ### getTimestamp Get the current timestamp of the P-Chain. **Function Signature:** ```typescript function getTimestamp(): Promise; interface GetTimestampReturnType { timestamp: string; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ------------------------ | ---------------- | | `GetTimestampReturnType` | Timestamp object | **Return Object:** | Property | Type | Description | | ----------- | -------- | ------------------------------------ | | `timestamp` | `string` | Current timestamp in ISO 8601 format | **Example:** ```typescript const timestamp = await client.pChain.getTimestamp(); console.log("Current timestamp:", timestamp.timestamp); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgettimestamp) - [getHeight](#getheight) - Get current height --- ## Fee Operations ### getFeeConfig Get the fee configuration for the P-Chain. **Function Signature:** ```typescript function getFeeConfig(): Promise; interface GetFeeConfigReturnType { weights: [ bandwidth: number, dbRead: number, dbWrite: number, compute: number, ]; maxCapacity: bigint; maxPerSecond: bigint; targetPerSecond: bigint; minPrice: bigint; excessConversionConstant: bigint; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ------------------------ | ------------------------ | | `GetFeeConfigReturnType` | Fee configuration object | **Return Object:** | Property | Type | Description | | -------------------------- | -------- | -------------------------------------------------- | | `weights` | `array` | Fee weights: [bandwidth, dbRead, dbWrite, compute] | | `maxCapacity` | `bigint` | Maximum capacity | | `maxPerSecond` | `bigint` | Maximum per second | | `targetPerSecond` | `bigint` | Target per second | | `minPrice` | `bigint` | Minimum price | | `excessConversionConstant` | `bigint` | Excess conversion constant | **Example:** ```typescript const feeConfig = await client.pChain.getFeeConfig(); console.log("Fee weights:", feeConfig.weights); console.log("Max capacity:", feeConfig.maxCapacity); console.log("Target per second:", feeConfig.targetPerSecond); console.log("Min price:", feeConfig.minPrice); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetfeeconfig) - [getFeeState](#getfeestate) - Get current fee state --- ### getFeeState Get the current fee state of the P-Chain. **Function Signature:** ```typescript function getFeeState(): Promise; interface GetFeeStateReturnType { capacity: bigint; excess: bigint; price: bigint; timestamp: string; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | ----------------------- | ---------------- | | `GetFeeStateReturnType` | Fee state object | **Return Object:** | Property | Type | Description | | ----------- | -------- | -------------------------- | | `capacity` | `bigint` | Current fee capacity | | `excess` | `bigint` | Current fee excess | | `price` | `bigint` | Current fee price | | `timestamp` | `string` | Timestamp of the fee state | **Example:** ```typescript const feeState = await client.pChain.getFeeState(); console.log("Fee capacity:", feeState.capacity); console.log("Fee excess:", feeState.excess); console.log("Fee price:", feeState.price); console.log("Timestamp:", feeState.timestamp); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetfeestate) - [getFeeConfig](#getfeeconfig) - Get fee configuration --- ## Supply Operations ### getCurrentSupply Get the current supply of AVAX tokens. **Function Signature:** ```typescript function getCurrentSupply( params?: GetCurrentSupplyParameters ): Promise; interface GetCurrentSupplyParameters { subnetId?: string; } interface GetCurrentSupplyReturnType { supply: bigint; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | -------------------------------------------------- | | `subnetId` | `string` | No | Subnet ID (defaults to Primary Network if omitted) | **Returns:** | Type | Description | | ---------------------------- | ------------- | | `GetCurrentSupplyReturnType` | Supply object | **Return Object:** | Property | Type | Description | | -------- | -------- | ---------------------------------------------- | | `supply` | `bigint` | Upper bound on the number of tokens that exist | **Example:** ```typescript // Get Primary Network supply const supply = await client.pChain.getCurrentSupply(); console.log("Primary Network supply:", supply.supply); // Get subnet-specific supply const subnetSupply = await client.pChain.getCurrentSupply({ subnetId: "11111111111111111111111111111111LpoYY", }); console.log("Subnet supply:", subnetSupply.supply); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetcurrentsupply) - [getBalance](#getbalance) - Get address balance --- ## Reward Operations ### getRewardUTXOs Get the reward UTXOs for a transaction. **Function Signature:** ```typescript function getRewardUTXOs( params: GetRewardUTXOsParameters ): Promise; interface GetRewardUTXOsParameters { txID: string; encoding?: "hex"; } interface GetRewardUTXOsReturnType { numFetched: number; utxos: string[]; encoding: "hex"; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ----------------------------------- | | `txID` | `string` | Yes | Transaction ID in CB58 format | | `encoding` | `"hex"` | No | Encoding format (defaults to "hex") | **Returns:** | Type | Description | | -------------------------- | ------------------- | | `GetRewardUTXOsReturnType` | Reward UTXOs object | **Return Object:** | Property | Type | Description | | ------------ | ---------- | ---------------------------------------- | | `numFetched` | `number` | Number of reward UTXOs fetched | | `utxos` | `string[]` | Array of reward UTXO bytes (hex encoded) | | `encoding` | `"hex"` | Encoding format used | **Example:** ```typescript const rewardUTXOs = await client.pChain.getRewardUTXOs({ txID: "11111111111111111111111111111111LpoYY", encoding: "hex", }); console.log("Reward UTXOs fetched:", rewardUTXOs.numFetched); console.log("UTXOs:", rewardUTXOs.utxos); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetrewardutxos) - [getUTXOs](#getutxos) - Get UTXOs for addresses --- ## L1 Validator Operations ### getL1Validator Get information about an L1 validator. **Function Signature:** ```typescript function getL1Validator( params: GetL1ValidatorParameters ): Promise; interface GetL1ValidatorParameters { validationID: string; } interface GetL1ValidatorReturnType { subnetID: string; nodeID: string; publicKey: string; remainingBalanceOwner: { addresses: string[]; locktime: string; threshold: string; }; deactivationOwner: { addresses: string[]; locktime: string; threshold: string; }; startTime: bigint; weight: bigint; minNonce?: bigint; balance?: bigint; height?: bigint; } ``` **Parameters:** | Name | Type | Required | Description | | -------------- | -------- | -------- | ------------------------------------------------------- | | `validationID` | `string` | Yes | The ID for L1 subnet validator registration transaction | **Returns:** | Type | Description | | -------------------------- | ------------------------------- | | `GetL1ValidatorReturnType` | L1 validator information object | **Return Object:** | Property | Type | Description | | ----------------------- | -------- | --------------------------------------------- | | `subnetID` | `string` | L1 subnet ID this validator is validating | | `nodeID` | `string` | Node ID of the validator | | `publicKey` | `string` | Compressed BLS public key of the validator | | `remainingBalanceOwner` | `object` | Owner that will receive any withdrawn balance | | `deactivationOwner` | `object` | Owner that can withdraw the balance | | `startTime` | `bigint` | Unix timestamp when validator was added | | `weight` | `bigint` | Weight used for consensus voting and ICM | | `minNonce` | `bigint` | Minimum nonce for SetL1ValidatorWeightTx | | `balance` | `bigint` | Current remaining balance for continuous fee | | `height` | `bigint` | Height of the last accepted block | **Example:** ```typescript const validator = await client.pChain.getL1Validator({ validationID: "11111111111111111111111111111111LpoYY", }); console.log("Subnet ID:", validator.subnetID); console.log("Node ID:", validator.nodeID); console.log("Weight:", validator.weight); console.log("Start time:", validator.startTime); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformgetl1validator) - [getCurrentValidators](#getcurrentvalidators) - Get current validators --- ## Chain Validation ### validatedBy Get the subnet that validates a given blockchain. **Function Signature:** ```typescript function validatedBy( params: ValidatedByParameters ): Promise; interface ValidatedByParameters { blockchainID: string; } interface ValidatedByReturnType { subnetID: string; } ``` **Parameters:** | Name | Type | Required | Description | | -------------- | -------- | -------- | ------------------- | | `blockchainID` | `string` | Yes | The blockchain's ID | **Returns:** | Type | Description | | ----------------------- | ---------------- | | `ValidatedByReturnType` | Subnet ID object | **Return Object:** | Property | Type | Description | | ---------- | -------- | ---------------------------------------------- | | `subnetID` | `string` | ID of the subnet that validates the blockchain | **Example:** ```typescript const validatedBy = await client.pChain.validatedBy({ blockchainID: "11111111111111111111111111111111LpoYY", }); console.log("Validated by subnet:", validatedBy.subnetID); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformvalidatedby) - [validates](#validates) - Get blockchains validated by subnet --- ### validates Get the IDs of the blockchains a subnet validates. **Function Signature:** ```typescript function validates(params: ValidatesParameters): Promise; interface ValidatesParameters { subnetID: string; } interface ValidatesReturnType { blockchainIDs: string[]; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | --------------- | | `subnetID` | `string` | Yes | The subnet's ID | **Returns:** | Type | Description | | --------------------- | --------------------- | | `ValidatesReturnType` | Blockchain IDs object | **Return Object:** | Property | Type | Description | | --------------- | ---------- | ----------------------------------------------- | | `blockchainIDs` | `string[]` | Array of blockchain IDs validated by the subnet | **Example:** ```typescript const validates = await client.pChain.validates({ subnetID: "11111111111111111111111111111111LpoYY", }); console.log("Number of blockchains:", validates.blockchainIDs.length); validates.blockchainIDs.forEach((blockchainID) => { console.log("Blockchain ID:", blockchainID); }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/p-chain#platformvalidates) - [validatedBy](#validatedby) - Get subnet validating blockchain ## Next Steps - **[P-Chain Wallet Methods](../wallet-methods/p-chain-wallet)** - Transaction preparation and signing - **[Wallet Client](../../clients/wallet-client)** - Complete wallet operations - **[Account Management](../../accounts)** - Account types and management # Public Methods (/docs/tooling/avalanche-sdk/client/methods/public-methods/public) --- title: Public Methods description: Complete reference for Avalanche-specific public client methods --- ## Overview The Avalanche Client extends viem's Public Client with Avalanche-specific methods for querying fee information, chain configuration, and active rules. These methods are available on both the main Avalanche Client and the C-Chain client. ## Fee Operations ### baseFee Get the base fee for the next block on the C-Chain. **Function Signature:** ```typescript function baseFee(): Promise; ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | -------- | -------------------------------------------------------------- | | `string` | Base fee for the next block as hex string (e.g., "0x3b9aca00") | **Example:** ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const baseFee = await client.baseFee(); console.log("Base fee:", baseFee); // "0x3b9aca00" ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#eth_basefee) - [maxPriorityFeePerGas](#maxpriorityfeepergas) - Get max priority fee per gas --- ### maxPriorityFeePerGas Get the maximum priority fee per gas for the next block. **Function Signature:** ```typescript function maxPriorityFeePerGas(): Promise; ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | -------- | --------------------------------------------------------------- | | `string` | Maximum priority fee per gas as hex string (e.g., "0x3b9aca00") | **Example:** ```typescript const maxPriorityFee = await client.maxPriorityFeePerGas(); console.log("Max priority fee per gas:", maxPriorityFee); // Use in EIP-1559 transaction const txHash = await walletClient.sendTransaction({ to: "0x...", value: avaxToWei(1), maxFeePerGas: baseFee + maxPriorityFee, maxPriorityFeePerGas: maxPriorityFee, }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#eth_maxpriorityfeepergas) - [baseFee](#basefee) - Get base fee --- ### feeConfig Get the fee configuration for a specific block. Returns fee settings and when they were last changed. **Function Signature:** ```typescript function feeConfig(params: FeeConfigParameters): Promise; interface FeeConfigParameters { blk?: string; // Block number or hash, defaults to "latest" } interface FeeConfigReturnType { feeConfig: { [key: string]: string; }; lastChangedAt: string; } ``` **Parameters:** | Name | Type | Required | Description | | ----- | -------- | -------- | ------------------------------------------------------- | | `blk` | `string` | No | Block number or hash (hex string), defaults to "latest" | **Returns:** | Type | Description | | --------------------- | ------------------------ | | `FeeConfigReturnType` | Fee configuration object | **Return Object:** | Property | Type | Description | | --------------- | -------- | ------------------------------------------ | | `feeConfig` | `object` | Fee configuration key-value pairs | | `lastChangedAt` | `string` | Timestamp when fee config was last changed | **Example:** ```typescript // Get fee config for latest block const feeConfig = await client.feeConfig({}); console.log("Fee config:", feeConfig.feeConfig); console.log("Last changed:", feeConfig.lastChangedAt); // Get fee config for specific block const blockFeeConfig = await client.feeConfig({ blk: "0x123456" }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/subnet-evm#eth_feeconfig) - [baseFee](#basefee) - Get base fee --- ## Chain Configuration ### getChainConfig Get the chain configuration for the C-Chain, including fork blocks and Avalanche-specific upgrade timestamps. **Function Signature:** ```typescript function getChainConfig(): Promise; interface GetChainConfigReturnType { chainId: number; homesteadBlock: number; daoForkBlock: number; daoForkSupport: boolean; eip150Block: number; eip150Hash: string; eip155Block: number; eip158Block: number; byzantiumBlock: number; constantinopleBlock: number; petersburgBlock: number; istanbulBlock: number; muirGlacierBlock: number; apricotPhase1BlockTimestamp: number; apricotPhase2BlockTimestamp: number; apricotPhase3BlockTimestamp: number; apricotPhase4BlockTimestamp: number; apricotPhase5BlockTimestamp: number; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | -------------------------- | -------------------------- | | `GetChainConfigReturnType` | Chain configuration object | **Return Object:** | Property | Type | Description | | ----------------------------- | --------- | --------------------------------- | | `chainId` | `number` | Chain ID | | `homesteadBlock` | `number` | Homestead fork block | | `daoForkBlock` | `number` | DAO fork block | | `daoForkSupport` | `boolean` | DAO fork support flag | | `eip150Block` | `number` | EIP-150 fork block | | `eip150Hash` | `string` | EIP-150 fork hash | | `eip155Block` | `number` | EIP-155 fork block | | `eip158Block` | `number` | EIP-158 fork block | | `byzantiumBlock` | `number` | Byzantium fork block | | `constantinopleBlock` | `number` | Constantinople fork block | | `petersburgBlock` | `number` | Petersburg fork block | | `istanbulBlock` | `number` | Istanbul fork block | | `muirGlacierBlock` | `number` | Muir Glacier fork block | | `apricotPhase1BlockTimestamp` | `number` | Apricot Phase 1 upgrade timestamp | | `apricotPhase2BlockTimestamp` | `number` | Apricot Phase 2 upgrade timestamp | | `apricotPhase3BlockTimestamp` | `number` | Apricot Phase 3 upgrade timestamp | | `apricotPhase4BlockTimestamp` | `number` | Apricot Phase 4 upgrade timestamp | | `apricotPhase5BlockTimestamp` | `number` | Apricot Phase 5 upgrade timestamp | **Example:** ```typescript const chainConfig = await client.getChainConfig(); console.log("Chain ID:", chainConfig.chainId); console.log("Apricot Phase 1:", chainConfig.apricotPhase1BlockTimestamp); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/c-chain#eth_getchainconfig) --- ## Active Rules ### getActiveRulesAt Get the active rules (EIPs, precompiles) at a specific timestamp. Useful for determining which features are enabled at a given time. **Function Signature:** ```typescript function getActiveRulesAt( params: GetActiveRulesAtParameters ): Promise; interface GetActiveRulesAtParameters { timestamp: string; // Unix timestamp as hex string or "latest" } interface GetActiveRulesAtReturnType { ethRules: Map; avalancheRules: Map; precompiles: Map; } ``` **Parameters:** | Name | Type | Required | Description | | ----------- | -------- | -------- | --------------------------------------------------------------- | | `timestamp` | `string` | Yes | Unix timestamp as hex string (e.g., "0x1234567890") or "latest" | **Returns:** | Type | Description | | ---------------------------- | ------------------- | | `GetActiveRulesAtReturnType` | Active rules object | **Return Object:** | Property | Type | Description | | ---------------- | --------------------- | -------------------------------------------- | | `ethRules` | `Map` | Active Ethereum rules (EIPs) | | `avalancheRules` | `Map` | Active Avalanche-specific rules | | `precompiles` | `Map` | Active precompiles with their configurations | **Example:** ```typescript // Get active rules at current time const activeRules = await client.getActiveRulesAt({ timestamp: "latest", }); console.log("Ethereum rules:", Array.from(activeRules.ethRules.keys())); console.log("Avalanche rules:", Array.from(activeRules.avalancheRules.keys())); console.log("Precompiles:", Array.from(activeRules.precompiles.keys())); // Get active rules at specific timestamp const historicalRules = await client.getActiveRulesAt({ timestamp: "0x1234567890", }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/subnet-evm#eth_getactiverulesat) --- ## Viem Integration The Avalanche Client extends viem's Public Client, providing access to all standard Ethereum RPC methods. For complete method reference, see: - **[viem Documentation](https://viem.sh/docs)** - Complete EVM method reference - **[viem Actions](https://viem.sh/docs/actions/public)** - Public client actions - **[viem Utilities](https://viem.sh/docs/utilities)** - Utility functions ## Next Steps - **[C-Chain Methods](c-chain)** - C-Chain-specific methods - **[Wallet Client](../clients/wallet-client)** - Transaction operations - **[Account Management](../../accounts)** - Account types and management # X-Chain Methods (/docs/tooling/avalanche-sdk/client/methods/public-methods/x-chain) --- title: X-Chain Methods description: Complete reference for X-Chain (Exchange Chain) methods --- ## Overview The X-Chain (Exchange Chain) is Avalanche's DAG-based chain designed for creating and trading digital smart assets. It handles asset creation, transfers, UTXO management, and provides the foundation for the Avalanche ecosystem. This reference covers all read-only X-Chain operations available through the Avalanche Client SDK. ## Balance Operations ### getBalance Get the balance of a specific asset controlled by a given address. **Function Signature:** ```typescript function getBalance( params: GetBalanceParameters ): Promise; interface GetBalanceParameters { address: string; assetID: string; } interface GetBalanceReturnType { balance: bigint; utxoIDs: { txID: string; outputIndex: number; }[]; } ``` **Parameters:** | Name | Type | Required | Description | | --------- | -------- | -------- | ------------------------ | | `address` | `string` | Yes | X-Chain address to query | | `assetID` | `string` | Yes | Asset ID to query | **Returns:** | Type | Description | | ---------------------- | -------------------------- | | `GetBalanceReturnType` | Balance information object | **Return Object:** | Property | Type | Description | | --------- | -------- | ----------------------------------------- | | `balance` | `bigint` | Balance amount for the specified asset | | `utxoIDs` | `array` | Array of UTXO IDs referencing the address | **Example:** ```typescript import { createAvalancheClient } from "@avalanche-sdk/client"; import { avalanche } from "@avalanche-sdk/client/chains"; const client = createAvalancheClient({ chain: avalanche, transport: { type: "http" }, }); const balance = await client.xChain.getBalance({ address: "X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5", assetID: "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", // AVAX }); console.log("Balance:", balance.balance); console.log("UTXO IDs:", balance.utxoIDs); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetbalance) - [getAllBalances](#getallbalances) - Get balances for all assets - [getUTXOs](#getutxos) - Get UTXOs for addresses --- ### getAllBalances Get the balances of all assets controlled by given addresses. **Function Signature:** ```typescript function getAllBalances( params: GetAllBalancesParameters ): Promise; interface GetAllBalancesParameters { addresses: string[]; } interface GetAllBalancesReturnType { balances: Array<{ assetID: string; balance: bigint; }>; } ``` **Parameters:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ----------------------------------- | | `addresses` | `string[]` | Yes | Array of X-Chain addresses to query | **Returns:** | Type | Description | | -------------------------- | --------------------- | | `GetAllBalancesReturnType` | Balances array object | **Return Object:** | Property | Type | Description | | ---------- | ------- | ------------------------------------------------- | | `balances` | `array` | Array of balance objects with assetID and balance | **Example:** ```typescript const allBalances = await client.xChain.getAllBalances({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], }); // Iterate over all assets allBalances.balances.forEach(({ assetID, balance }) => { console.log(`Asset ${assetID}: ${balance}`); }); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetallbalances) - [getBalance](#getbalance) - Get balance for specific asset - [getAssetDescription](#getassetdescription) - Get asset information --- ## Asset Operations ### getAssetDescription Get information about an asset. **Function Signature:** ```typescript function getAssetDescription( params: GetAssetDescriptionParameters ): Promise; interface GetAssetDescriptionParameters { assetID: string; } interface GetAssetDescriptionReturnType { assetID: string; name: string; symbol: string; denomination: number; } ``` **Parameters:** | Name | Type | Required | Description | | --------- | -------- | -------- | ----------- | | `assetID` | `string` | Yes | Asset ID | **Returns:** | Type | Description | | ------------------------------- | ------------------------ | | `GetAssetDescriptionReturnType` | Asset information object | **Return Object:** | Property | Type | Description | | -------------- | -------- | --------------------------------------------- | | `assetID` | `string` | Asset ID | | `name` | `string` | Asset name | | `symbol` | `string` | Asset symbol | | `denomination` | `number` | Asset denomination (number of decimal places) | **Example:** ```typescript const asset = await client.xChain.getAssetDescription({ assetID: "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", }); console.log("Asset name:", asset.name); console.log("Asset symbol:", asset.symbol); console.log("Denomination:", asset.denomination); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetassetdescription) - [getBalance](#getbalance) - Get balance for this asset --- ## Block Operations ### getHeight Get the current block height of the X-Chain. **Function Signature:** ```typescript function getHeight(): Promise; interface GetHeightReturnType { height: number; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | --------------------- | ------------- | | `GetHeightReturnType` | Height object | **Return Object:** | Property | Type | Description | | -------- | -------- | ---------------------------- | | `height` | `number` | Current X-Chain block height | **Example:** ```typescript const height = await client.xChain.getHeight(); console.log("Current X-Chain height:", height.height); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetheight) - [getBlockByHeight](#getblockbyheight) - Get block at height --- ### getBlockByHeight Get a block by its height. **Function Signature:** ```typescript function getBlockByHeight( params: GetBlockByHeightParameters ): Promise; interface GetBlockByHeightParameters { height: number; encoding?: "hex" | "json"; } interface GetBlockByHeightReturnType { encoding: "hex" | "json"; block: string | object; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ----------------- | -------- | ------------------------------------ | | `height` | `number` | Yes | Block height | | `encoding` | `"hex" \| "json"` | No | Encoding format (defaults to "json") | **Returns:** | Type | Description | | ---------------------------- | ----------------- | | `GetBlockByHeightReturnType` | Block data object | **Return Object:** | Property | Type | Description | | ---------- | ------------------ | ------------------------------------------- | | `encoding` | `"hex" \| "json"` | Encoding format used | | `block` | `string \| object` | Block data in the specified encoding format | **Example:** ```typescript const block = await client.xChain.getBlockByHeight({ height: 12345, encoding: "hex", }); console.log("Block data:", block.block); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetblockbyheight) - [getBlock](#getblock) - Get block by ID - [getHeight](#getheight) - Get current height --- ### getBlock Get a block by its ID. **Function Signature:** ```typescript function getBlock(params: GetBlockParameters): Promise; interface GetBlockParameters { blockId: string; encoding?: "hex" | "json"; } interface GetBlockReturnType { encoding: "hex" | "json"; block: string | object; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ----------------- | -------- | ------------------------------------ | | `blockId` | `string` | Yes | Block ID in CB58 format | | `encoding` | `"hex" \| "json"` | No | Encoding format (defaults to "json") | **Returns:** | Type | Description | | -------------------- | ----------------- | | `GetBlockReturnType` | Block data object | **Return Object:** | Property | Type | Description | | ---------- | ------------------ | ------------------------------------------- | | `encoding` | `"hex" \| "json"` | Encoding format used | | `block` | `string \| object` | Block data in the specified encoding format | **Example:** ```typescript const block = await client.xChain.getBlock({ blockId: "block-id-in-cb58-format", encoding: "hex", }); console.log("Block:", block.block); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetblock) - [getBlockByHeight](#getblockbyheight) - Get block by height --- ## Transaction Operations ### getTx Get a transaction by its ID. **Function Signature:** ```typescript function getTx(params: GetTxParameters): Promise; interface GetTxParameters { txID: string; encoding?: "hex" | "json"; } interface GetTxReturnType { encoding: "hex" | "json"; tx: string | object; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | ----------------- | -------- | ------------------------------------ | | `txID` | `string` | Yes | Transaction ID in CB58 format | | `encoding` | `"hex" \| "json"` | No | Encoding format (defaults to "json") | **Returns:** | Type | Description | | ----------------- | ----------------------- | | `GetTxReturnType` | Transaction data object | **Return Object:** | Property | Type | Description | | ---------- | ------------------ | ------------------------------------------------- | | `encoding` | `"hex" \| "json"` | Encoding format used | | `tx` | `string \| object` | Transaction data in the specified encoding format | **Example:** ```typescript const tx = await client.xChain.getTx({ txID: "transaction-id", encoding: "hex", }); console.log("Transaction:", tx.tx); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgettx) - [getTxStatus](#gettxstatus) - Get transaction status - [issueTx](#issuetx) - Submit transaction --- ### getTxStatus Get the status of a transaction. **Function Signature:** ```typescript function getTxStatus( params: GetTxStatusParameters ): Promise; interface GetTxStatusParameters { txID: string; includeReason?: boolean | true; } interface GetTxStatusReturnType { status: "Accepted" | "Processing" | "Rejected" | "Unknown"; reason?: string; } ``` **Parameters:** | Name | Type | Required | Description | | --------------- | ----------------- | -------- | -------------------------------------------- | | `txID` | `string` | Yes | Transaction ID in CB58 format | | `includeReason` | `boolean \| true` | No | Whether to include the reason for the status | **Returns:** | Type | Description | | ----------------------- | ------------------------- | | `GetTxStatusReturnType` | Transaction status object | **Return Object:** | Property | Type | Description | | -------- | -------- | ---------------------------------------------------------------------- | | `status` | `string` | Transaction status: "Accepted", "Processing", "Rejected", or "Unknown" | | `reason` | `string` | Optional reason for the status (if rejected) | **Status Values:** - **Accepted**: Transaction has been accepted and included in a block - **Processing**: Transaction is being processed - **Rejected**: Transaction was rejected - **Unknown**: Transaction status cannot be determined **Example:** ```typescript const status = await client.xChain.getTxStatus({ txID: "transaction-id", }); if (status.status === "Accepted") { console.log("Transaction accepted!"); } else if (status.status === "Rejected") { console.log("Transaction rejected"); if (status.reason) { console.log("Reason:", status.reason); } } else { console.log("Transaction status:", status.status); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgettxstatus) - [getTx](#gettx) - Get transaction details --- ### getTxFee Get the transaction fee for the X-Chain. **Function Signature:** ```typescript function getTxFee(): Promise; interface GetTxFeeReturnType { txFee: number; createAssetTxFee: number; } ``` **Parameters:** No parameters required. **Returns:** | Type | Description | | -------------------- | ---------------------- | | `GetTxFeeReturnType` | Transaction fee object | **Return Object:** | Property | Type | Description | | ------------------ | -------- | ---------------------------- | | `txFee` | `number` | Standard transaction fee | | `createAssetTxFee` | `number` | Fee for creating a new asset | **Example:** ```typescript const fees = await client.xChain.getTxFee(); console.log("Standard transaction fee:", fees.txFee); console.log("Create asset fee:", fees.createAssetTxFee); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgettxfee) --- ## UTXO Operations ### getUTXOs Get the UTXOs controlled by a set of addresses. **Function Signature:** ```typescript function getUTXOs(params: GetUTXOsParameters): Promise; interface GetUTXOsParameters { addresses: string[]; sourceChain?: string; limit?: number; startIndex?: { address: string; utxo: string; }; encoding?: "hex"; } interface GetUTXOsReturnType { numFetched: number; utxos: string[]; endIndex: { address: string; utxo: string; }; sourceChain?: string; encoding: "hex"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------- | ----------------------------------- | -------- | ----------------------------------------------- | | `addresses` | `string[]` | Yes | Array of X-Chain addresses | | `sourceChain` | `string` | No | Source chain ID (e.g., "P" for P-Chain) | | `limit` | `number` | No | Maximum number of UTXOs to return | | `startIndex` | `{ address: string; utxo: string }` | No | Pagination cursor for next page | | `encoding` | `"hex"` | No | Encoding format (can only be "hex" if provided) | **Returns:** | Type | Description | | -------------------- | ------------------------- | | `GetUTXOsReturnType` | UTXO data with pagination | **Return Object:** | Property | Type | Description | | ------------- | ----------------------------------- | ---------------------------------------- | | `numFetched` | `number` | Number of UTXOs fetched in this response | | `utxos` | `string[]` | Array of UTXO bytes (hex encoded) | | `endIndex` | `{ address: string; utxo: string }` | Pagination cursor for fetching next page | | `sourceChain` | `string` | Source chain ID (if specified) | | `encoding` | `"hex"` | Encoding format used | **Example:** ```typescript // Get first page const utxos = await client.xChain.getUTXOs({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], limit: 100, }); console.log("Number of UTXOs:", utxos.numFetched); // Get next page if needed if (utxos.endIndex) { const moreUTXOs = await client.xChain.getUTXOs({ addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], startIndex: utxos.endIndex, limit: 100, }); } ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetutxos) - [getBalance](#getbalance) - Get balance summary --- ## Transaction Submission ### issueTx Issue a transaction to the X-Chain. **Function Signature:** ```typescript function issueTx(params: IssueTxParameters): Promise; interface IssueTxParameters { tx: string; encoding: "hex"; } interface IssueTxReturnType { txID: string; } ``` **Parameters:** | Name | Type | Required | Description | | ---------- | -------- | -------- | ------------------------------- | | `tx` | `string` | Yes | Transaction bytes in hex format | | `encoding` | `"hex"` | Yes | Encoding format (must be "hex") | **Returns:** | Type | Description | | ------------------- | --------------------- | | `IssueTxReturnType` | Transaction ID object | **Return Object:** | Property | Type | Description | | -------- | -------- | ----------------------------- | | `txID` | `string` | Transaction ID in CB58 format | **Example:** ```typescript const txID = await client.xChain.issueTx({ tx: "0x00000009de31b4d8b22991d51aa6aa1fc733f23a851a8c9400000000000186a0...", encoding: "hex", }); console.log("Transaction ID:", txID.txID); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmissuetx) - [getTxStatus](#gettxstatus) - Check transaction status --- ## Genesis Operations ### buildGenesis Build a genesis block for a custom blockchain. **Function Signature:** ```typescript function buildGenesis( params: BuildGenesisParameters ): Promise; interface BuildGenesisParameters { networkID: number; genesisData: { name: string; symbol: string; denomination: number; initialState: { fixedCap: { amount: number; addresses: string[]; }; }; }; encoding: "hex"; } interface BuildGenesisReturnType { bytes: string; encoding: "hex"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------- | -------- | -------- | ------------------------------------------- | | `networkID` | `number` | Yes | Network ID | | `genesisData` | `object` | Yes | Genesis block data with asset configuration | | `encoding` | `"hex"` | Yes | Encoding format (must be "hex") | **Returns:** | Type | Description | | ------------------------ | -------------------------- | | `BuildGenesisReturnType` | Genesis block bytes object | **Return Object:** | Property | Type | Description | | ---------- | -------- | --------------------------------- | | `bytes` | `string` | Genesis block bytes in hex format | | `encoding` | `"hex"` | Encoding format used | **Example:** ```typescript const genesis = await client.xChain.buildGenesis({ networkID: 16, genesisData: { name: "myFixedCapAsset", symbol: "MFCA", denomination: 0, initialState: { fixedCap: { amount: 100000, addresses: ["X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"], }, }, }, encoding: "hex", }); console.log("Genesis bytes:", genesis.bytes); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmbuildgenesis) --- ## Next Steps - **[X-Chain Wallet Methods](../wallet-methods/x-chain-wallet)** - Transaction preparation and signing - **[Wallet Client](../../clients/wallet-client)** - Complete wallet operations - **[Account Management](../../accounts)** - Account types and management # C-Chain Wallet Methods (/docs/tooling/avalanche-sdk/client/methods/wallet-methods/c-chain-wallet) --- title: C-Chain Wallet Methods description: Complete reference for C-Chain atomic transaction methods --- ## Overview The C-Chain Wallet Methods provide transaction preparation capabilities for atomic cross-chain transfers between the C-Chain and other Avalanche chains (P-Chain and X-Chain). These methods handle the export and import of native AVAX via atomic transactions. **Access:** `walletClient.cChain` ## prepareExportTxn Prepare a transaction to export AVAX from C-Chain to another chain (P-Chain or X-Chain). **Function Signature:** ```typescript function prepareExportTxn( params: PrepareExportTxnParameters ): Promise; interface PrepareExportTxnParameters { destinationChain: "P" | "X"; fromAddress: string; exportedOutput: { addresses: string[]; amount: bigint; locktime?: bigint; threshold?: number; }; context?: Context; } interface PrepareExportTxnReturnType { tx: UnsignedTx; exportTx: ExportTx; chainAlias: "C"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------------ | ------------ | -------- | --------------------------------------------------- | | `destinationChain` | `"P" \| "X"` | Yes | Chain alias to export funds to (P-Chain or X-Chain) | | `fromAddress` | `string` | Yes | EVM address to export funds from | | `exportedOutput` | `object` | Yes | Consolidated exported output (UTXO) | | `context` | `Context` | No | Optional context for the transaction | **Exported Output Object:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ------------------------------------------------------------------ | | `addresses` | `string[]` | Yes | Addresses who can sign the consuming of this UTXO | | `amount` | `bigint` | Yes | Amount in nano AVAX held by this exported output | | `locktime` | `bigint` | No | Timestamp in seconds after which this UTXO can be consumed | | `threshold` | `number` | No | Threshold of `addresses`' signatures required to consume this UTXO | **Returns:** | Type | Description | | ---------------------------- | ------------------------- | | `PrepareExportTxnReturnType` | Export transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `exportTx` | `ExportTx` | The export transaction instance | | `chainAlias` | `"C"` | The chain alias | **Example:** ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Export from C-Chain to P-Chain const exportTx = await walletClient.cChain.prepareExportTxn({ destinationChain: "P", fromAddress: account.getEVMAddress(), exportedOutput: { addresses: [account.getXPAddress("P")], amount: avaxToNanoAvax(0.001), }, }); // Sign and send const { txHash } = await walletClient.sendXPTransaction({ txOrTxHex: exportTx, chainAlias: "C", }); console.log("Export transaction hash:", txHash); ``` **Related:** - [prepareImportTxn](#prepareimporttxn) - Import to C-Chain - [Wallet send method](./wallet#send) - Simplified cross-chain transfers - [getAtomicTxStatus](../public-methods/c-chain#getatomictxstatus) - Check transaction status --- ## prepareImportTxn Prepare a transaction to import AVAX from another chain (P-Chain or X-Chain) to C-Chain. **Function Signature:** ```typescript function prepareImportTxn( params: PrepareImportTxnParameters ): Promise; interface PrepareImportTxnParameters { account?: AvalancheAccount | Address | undefined; sourceChain: "P" | "X"; toAddress: string; fromAddresses?: string[]; utxos?: Utxo[]; context?: Context; } interface PrepareImportTxnReturnType { tx: UnsignedTx; importTx: ImportTx; chainAlias: "C"; } ``` **Parameters:** | Name | Type | Required | Description | | --------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------- | | `account` | `AvalancheAccount \| Address` | No | Account to use for the transaction | | `sourceChain` | `"P" \| "X"` | Yes | Chain alias to import funds from (P-Chain or X-Chain) | | `toAddress` | `string` | Yes | EVM address to import funds to | | `fromAddresses` | `string[]` | No | Addresses to import funds from (auto-fetched if not provided) | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs (must be in atomic memory, auto-fetched if not provided) | | `context` | `Context` | No | Optional context for the transaction | **Returns:** | Type | Description | | ---------------------------- | ------------------------- | | `PrepareImportTxnReturnType` | Import transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `importTx` | `ImportTx` | The import transaction instance | | `chainAlias` | `"C"` | The chain alias | **Example:** ```typescript const importTx = await walletClient.cChain.prepareImportTxn({ sourceChain: "P", toAddress: account.getEVMAddress(), fromAddresses: [account.getXPAddress("P")], }); // Sign and send const { txHash } = await walletClient.sendXPTransaction({ txOrTxHex: importTx, chainAlias: "C", }); console.log("Import transaction hash:", txHash); ``` **Related:** - [prepareExportTxn](#prepareexporttxn) - Export from C-Chain - [getAtomicTxStatus](../public-methods/c-chain#getatomictxstatus) - Check transaction status --- ## Complete Cross-Chain Transfer Workflow ### Export from C-Chain to P-Chain ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // 1. Export from C-Chain const exportTx = await walletClient.cChain.prepareExportTxn({ destinationChain: "P", fromAddress: account.getEVMAddress(), exportedOutput: { addresses: [account.getXPAddress("P")], amount: avaxToNanoAvax(1.0), }, }); // 2. Sign and send export transaction const { txHash: exportTxHash } = await walletClient.sendXPTransaction({ txOrTxHex: exportTx, chainAlias: "C", }); // 3. Wait for export to be committed await walletClient.waitForTxn({ txHash: exportTxHash, chainAlias: "C", }); console.log("Export completed:", exportTxHash); // 4. Import to P-Chain const importTx = await walletClient.pChain.prepareImportTxn({ sourceChain: "C", toAddresses: [account.getXPAddress("P")], }); const { txHash: importTxHash } = await walletClient.sendXPTransaction({ txOrTxHex: importTx, chainAlias: "P", }); console.log("Import completed:", importTxHash); ``` --- ## Next Steps - **[Wallet Methods](./wallet)** - General wallet operations - **[P-Chain Wallet Methods](./p-chain-wallet)** - P-Chain transaction preparation - **[X-Chain Wallet Methods](./x-chain-wallet)** - X-Chain transaction preparation - **[Account Management](../accounts)** - Account types and management # P-Chain Wallet Methods (/docs/tooling/avalanche-sdk/client/methods/wallet-methods/p-chain-wallet) --- title: P-Chain Wallet Methods description: Complete reference for P-Chain transaction preparation methods --- ## Overview The P-Chain Wallet Methods provide transaction preparation capabilities for the Platform Chain. These methods allow you to create unsigned transactions for various operations including base transfers, validator operations, delegator operations, subnet management, and cross-chain transfers. **Access:** `walletClient.pChain` ## prepareBaseTxn Prepare a base P-Chain transaction for transferring AVAX. **Function Signature:** ```typescript function prepareBaseTxn( params: PrepareBaseTxnParameters ): Promise; interface PrepareBaseTxnParameters { outputs?: Output[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface Output { addresses: string[]; amount: bigint; assetId?: string; locktime?: bigint; threshold?: number; } interface PrepareBaseTxnReturnType { tx: UnsignedTx; baseTx: BaseTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------- | -------- | --------------------------------------------- | | `outputs` | `Output[]` | No | Array of outputs to send funds to | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Output Object:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ------------------------------------------------------------------ | | `addresses` | `string[]` | Yes | Addresses who can sign the consuming of this UTXO | | `amount` | `bigint` | Yes | Amount in nano AVAX | | `assetId` | `string` | No | Asset ID of the UTXO | | `locktime` | `bigint` | No | Timestamp in seconds after which this UTXO can be consumed | | `threshold` | `number` | No | Threshold of `addresses`' signatures required to consume this UTXO | **Returns:** | Type | Description | | -------------------------- | ----------------------- | | `PrepareBaseTxnReturnType` | Base transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ----------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `baseTx` | `BaseTx` | The base transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); const unsignedTx = await walletClient.pChain.prepareBaseTxn({ outputs: [ { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], amount: avaxToNanoAvax(1), }, ], }); // Sign and send const signedTx = await walletClient.signXPTransaction({ tx: unsignedTx.tx, chainAlias: "P", }); const { txHash } = await walletClient.sendXPTransaction({ txOrTxHex: signedTx.signedTxHex, chainAlias: "P", }); console.log("Transaction hash:", txHash); ``` **Related:** - [prepareExportTxn](#prepareexporttxn) - Cross-chain exports - [prepareImportTxn](#prepareimporttxn) - Cross-chain imports --- ## prepareAddPermissionlessValidatorTxn Prepare a transaction to add a permissionless validator to the Primary Network. **Function Signature:** ```typescript function prepareAddPermissionlessValidatorTxn( params: PrepareAddPermissionlessValidatorTxnParameters ): Promise; interface PrepareAddPermissionlessValidatorTxnParameters { nodeId: string; stakeInAvax: bigint; end: bigint; rewardAddresses: string[]; delegatorRewardAddresses: string[]; delegatorRewardPercentage: number; publicKey?: string; signature?: string; threshold?: number; locktime?: bigint; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareAddPermissionlessValidatorTxnReturnType { tx: UnsignedTx; addPermissionlessValidatorTx: AddPermissionlessValidatorTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | --------------------------- | ---------- | -------- | --------------------------------------------------------------------------------- | | `nodeId` | `string` | Yes | Node ID of the validator being added | | `stakeInAvax` | `bigint` | Yes | Amount of AVAX to stake (in nano AVAX) | | `end` | `bigint` | Yes | Unix time in seconds when validator will be removed | | `rewardAddresses` | `string[]` | Yes | Addresses which will receive validator rewards | | `delegatorRewardAddresses` | `string[]` | Yes | Addresses which will receive delegator fee rewards | | `delegatorRewardPercentage` | `number` | Yes | Percentage of delegator rewards as delegation fee (2-100, up to 3 decimal places) | | `publicKey` | `string` | No | BLS public key (in hex format) | | `signature` | `string` | No | BLS signature (in hex format) | | `threshold` | `number` | No | Number of signatures required to spend reward UTXO (default: 1) | | `locktime` | `bigint` | No | Unix timestamp after which reward UTXO can be spent (default: 0) | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ------------------------------------------------ | -------------------------------- | | `PrepareAddPermissionlessValidatorTxnReturnType` | Add validator transaction object | **Return Object:** | Property | Type | Description | | ------------------------------ | ------------------------------ | -------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `addPermissionlessValidatorTx` | `AddPermissionlessValidatorTx` | The add validator transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript import { avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const validatorTx = await walletClient.pChain.prepareAddPermissionlessValidatorTxn({ nodeId: "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", stakeInAvax: avaxToNanoAvax(2000), end: BigInt(1716441600), rewardAddresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], delegatorRewardAddresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], delegatorRewardPercentage: 2.5, threshold: 1, }); // Sign and send const signedTx = await walletClient.signXPTransaction({ tx: validatorTx.tx, chainAlias: "P", }); const { txHash } = await walletClient.sendXPTransaction({ txOrTxHex: signedTx.signedTxHex, chainAlias: "P", }); ``` **Related:** - [prepareAddSubnetValidatorTxn](#prepareaddsubnetvalidatortxn) - Add to subnet - [prepareAddPermissionlessDelegatorTxn](#prepareaddpermissionlessdelegatortxn) - Add delegator --- ## prepareAddPermissionlessDelegatorTxn Prepare a transaction to add a permissionless delegator to a validator. **Function Signature:** ```typescript function prepareAddPermissionlessDelegatorTxn( params: PrepareAddPermissionlessDelegatorTxnParameters ): Promise; interface PrepareAddPermissionlessDelegatorTxnParameters { nodeId: string; stakeInAvax: bigint; end: bigint; rewardAddresses: string[]; threshold?: number; locktime?: bigint; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareAddPermissionlessDelegatorTxnReturnType { tx: UnsignedTx; addPermissionlessDelegatorTx: AddPermissionlessDelegatorTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------- | -------- | ---------------------------------------------------------------- | | `nodeId` | `string` | Yes | Node ID of the validator to delegate to | | `stakeInAvax` | `bigint` | Yes | Amount of AVAX to stake (in nano AVAX) | | `end` | `bigint` | Yes | Unix time in seconds when delegation stops | | `rewardAddresses` | `string[]` | Yes | Addresses which will receive rewards | | `threshold` | `number` | No | Number of signatures required to spend reward UTXO (default: 1) | | `locktime` | `bigint` | No | Unix timestamp after which reward UTXO can be spent (default: 0) | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ------------------------------------------------ | -------------------------------- | | `PrepareAddPermissionlessDelegatorTxnReturnType` | Add delegator transaction object | **Return Object:** | Property | Type | Description | | ------------------------------ | ------------------------------ | -------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `addPermissionlessDelegatorTx` | `AddPermissionlessDelegatorTx` | The add delegator transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const delegatorTx = await walletClient.pChain.prepareAddPermissionlessDelegatorTxn({ nodeId: "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", stakeInAvax: avaxToNanoAvax(25), end: BigInt(1716441600), rewardAddresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], threshold: 1, }); ``` **Related:** - [prepareAddPermissionlessValidatorTxn](#prepareaddpermissionlessvalidatortxn) - Add validator --- ## prepareExportTxn Prepare a transaction to export AVAX from P-Chain to another chain. **Function Signature:** ```typescript function prepareExportTxn( params: PrepareExportTxnParameters ): Promise; interface PrepareExportTxnParameters { destinationChain: "X" | "C"; exportedOutputs: Output[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareExportTxnReturnType { tx: UnsignedTx; exportTx: ExportTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------------ | ------------ | -------- | --------------------------------------------- | | `destinationChain` | `"X" \| "C"` | Yes | Chain alias to export funds to | | `exportedOutputs` | `Output[]` | Yes | Outputs to export | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ---------------------------- | ------------------------- | | `PrepareExportTxnReturnType` | Export transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `exportTx` | `ExportTx` | The export transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const exportTx = await walletClient.pChain.prepareExportTxn({ destinationChain: "C", exportedOutputs: [ { addresses: [account.getEVMAddress()], amount: avaxToNanoAvax(0.001), }, ], }); // Sign and send const signedTx = await walletClient.signXPTransaction({ tx: exportTx.tx, chainAlias: "P", }); const { txHash } = await walletClient.sendXPTransaction({ txOrTxHex: signedTx.signedTxHex, chainAlias: "P", }); ``` **Related:** - [prepareImportTxn](#prepareimporttxn) - Import to P-Chain - [Wallet send method](./wallet#send) - Simplified cross-chain transfers --- ## prepareImportTxn Prepare a transaction to import AVAX from another chain to P-Chain. **Function Signature:** ```typescript function prepareImportTxn( params: PrepareImportTxnParameters ): Promise; interface PrepareImportTxnParameters { sourceChain: "X" | "C"; importedOutput: ImportedOutput; fromAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface ImportedOutput { addresses: string[]; locktime?: bigint; threshold?: number; } interface PrepareImportTxnReturnType { tx: UnsignedTx; importTx: ImportTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------------- | -------- | ----------------------------------------------- | | `sourceChain` | `"X" \| "C"` | Yes | Chain alias to import funds from | | `importedOutput` | `ImportedOutput` | Yes | Consolidated imported output from atomic memory | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Imported Output Object:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ----------------------------------------------------------------------------------- | | `addresses` | `string[]` | Yes | Addresses who can sign the consuming of this UTXO | | `locktime` | `bigint` | No | Timestamp in seconds after which this UTXO can be consumed | | `threshold` | `number` | No | Number of signatures required out of total `addresses` to spend the imported output | **Returns:** | Type | Description | | ---------------------------- | ------------------------- | | `PrepareImportTxnReturnType` | Import transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `importTx` | `ImportTx` | The import transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const importTx = await walletClient.pChain.prepareImportTxn({ sourceChain: "C", importedOutput: { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], threshold: 1, }, }); // Sign and send const signedTx = await walletClient.signXPTransaction({ tx: importTx.tx, chainAlias: "P", }); const { txHash } = await walletClient.sendXPTransaction({ txOrTxHex: signedTx.signedTxHex, chainAlias: "P", }); ``` **Related:** - [prepareExportTxn](#prepareexporttxn) - Export from P-Chain --- ## prepareAddSubnetValidatorTxn Prepare a transaction to add a validator to a subnet. **Function Signature:** ```typescript function prepareAddSubnetValidatorTxn( params: PrepareAddSubnetValidatorTxnParameters ): Promise; interface PrepareAddSubnetValidatorTxnParameters { subnetId: string; nodeId: string; weight: bigint; end: bigint; subnetAuth: readonly number[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareAddSubnetValidatorTxnReturnType { tx: UnsignedTx; addSubnetValidatorTx: AddSubnetValidatorTx; subnetOwners: PChainOwner; subnetAuth: number[]; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ------------------- | -------- | -------------------------------------------------------------------------- | | `subnetId` | `string` | Yes | Subnet ID to add the validator to | | `nodeId` | `string` | Yes | Node ID of the validator being added | | `weight` | `bigint` | Yes | Weight of the validator used during consensus | | `end` | `bigint` | Yes | End timestamp in seconds after which validator will be removed | | `subnetAuth` | `readonly number[]` | Yes | Array of indices from subnet's owners array who will sign this transaction | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ---------------------------------------- | --------------------------------------- | | `PrepareAddSubnetValidatorTxnReturnType` | Add subnet validator transaction object | **Return Object:** | Property | Type | Description | | ---------------------- | ---------------------- | --------------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `addSubnetValidatorTx` | `AddSubnetValidatorTx` | The add subnet validator transaction instance | | `subnetOwners` | `PChainOwner` | The subnet owners | | `subnetAuth` | `number[]` | Array of indices from subnet's owners array | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const addSubnetValidatorTx = await walletClient.pChain.prepareAddSubnetValidatorTxn({ subnetId: "2b175hLJhGdj3CzgXNUHXDPVY3wQo3y3VWqPjKpF5vK", nodeId: "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", weight: BigInt(1000000), end: BigInt(1716441600), subnetAuth: [0, 1], }); ``` **Related:** - [prepareRemoveSubnetValidatorTxn](#prepareremovesubnetvalidatortxn) - Remove subnet validator --- ## prepareRemoveSubnetValidatorTxn Prepare a transaction to remove a validator from a subnet. **Function Signature:** ```typescript function prepareRemoveSubnetValidatorTxn( params: PrepareRemoveSubnetValidatorTxnParameters ): Promise; interface PrepareRemoveSubnetValidatorTxnParameters { subnetId: string; nodeId: string; subnetAuth: readonly number[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareRemoveSubnetValidatorTxnReturnType { tx: UnsignedTx; removeSubnetValidatorTx: RemoveSubnetValidatorTx; subnetOwners: PChainOwner; subnetAuth: number[]; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ------------------- | -------- | -------------------------------------------------------------------------- | | `subnetId` | `string` | Yes | Subnet ID to remove the validator from | | `nodeId` | `string` | Yes | Node ID of the validator being removed | | `subnetAuth` | `readonly number[]` | Yes | Array of indices from subnet's owners array who will sign this transaction | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ------------------------------------------- | ------------------------------------------ | | `PrepareRemoveSubnetValidatorTxnReturnType` | Remove subnet validator transaction object | **Return Object:** | Property | Type | Description | | ------------------------- | ------------------------- | ------------------------------------------------ | | `tx` | `UnsignedTx` | The unsigned transaction | | `removeSubnetValidatorTx` | `RemoveSubnetValidatorTx` | The remove subnet validator transaction instance | | `subnetOwners` | `PChainOwner` | The subnet owners | | `subnetAuth` | `number[]` | Array of indices from subnet's owners array | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const removeSubnetValidatorTx = await walletClient.pChain.prepareRemoveSubnetValidatorTxn({ subnetId: "2b175hLJhGdj3CzgXNUHXDPVY3wQo3y3VWqPjKpF5vK", nodeId: "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", subnetAuth: [0, 1], }); ``` **Related:** - [prepareAddSubnetValidatorTxn](#prepareaddsubnetvalidatortxn) - Add subnet validator --- ## prepareCreateSubnetTxn Prepare a transaction to create a new subnet. **Function Signature:** ```typescript function prepareCreateSubnetTxn( params: PrepareCreateSubnetTxnParameters ): Promise; interface PrepareCreateSubnetTxnParameters { subnetOwners: SubnetOwners; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface SubnetOwners { addresses: string[]; threshold?: number; locktime?: bigint; } interface PrepareCreateSubnetTxnReturnType { tx: UnsignedTx; createSubnetTx: CreateSubnetTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | -------------- | -------- | --------------------------------------------- | | `subnetOwners` | `SubnetOwners` | Yes | Subnet owners configuration | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Subnet Owners Object:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ---------------------------------------------------------------------------------------- | | `addresses` | `string[]` | Yes | List of unique addresses (must be sorted lexicographically) | | `threshold` | `number` | No | Number of unique signatures required to spend the output (must be ≤ length of addresses) | | `locktime` | `bigint` | No | Unix timestamp after which the output can be spent | **Returns:** | Type | Description | | ---------------------------------- | -------------------------------- | | `PrepareCreateSubnetTxnReturnType` | Create subnet transaction object | **Return Object:** | Property | Type | Description | | ---------------- | ---------------- | -------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `createSubnetTx` | `CreateSubnetTx` | The create subnet transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const createSubnetTx = await walletClient.pChain.prepareCreateSubnetTxn({ subnetOwners: { addresses: [ "P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz", "P-fuji1y8zrxh9cvdny0e8n8n4n7h4q4h4q4h4q4h4q4h", ], threshold: 2, }, }); ``` **Related:** - [prepareCreateChainTxn](#preparecreatechaintxn) - Create chain on subnet --- ## prepareCreateChainTxn Prepare a transaction to create a new blockchain on a subnet. **Function Signature:** ```typescript function prepareCreateChainTxn( params: PrepareCreateChainTxnParameters ): Promise; interface PrepareCreateChainTxnParameters { subnetId: string; vmId: string; chainName: string; genesisData: Record; subnetAuth: readonly number[]; fxIds?: readonly string[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareCreateChainTxnReturnType { tx: UnsignedTx; createChainTx: CreateChainTx; subnetOwners: PChainOwner; subnetAuth: number[]; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ------------------------- | -------- | -------------------------------------------------------------------------- | | `subnetId` | `string` | Yes | Subnet ID to create the chain on | | `vmId` | `string` | Yes | VM ID of the chain being created | | `chainName` | `string` | Yes | Name of the chain being created | | `genesisData` | `Record` | Yes | Genesis JSON data of the chain being created | | `subnetAuth` | `readonly number[]` | Yes | Array of indices from subnet's owners array who will sign this transaction | | `fxIds` | `readonly string[]` | No | Array of FX IDs to be added to the chain | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | --------------------------------- | ------------------------------- | | `PrepareCreateChainTxnReturnType` | Create chain transaction object | **Return Object:** | Property | Type | Description | | --------------- | --------------- | ------------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `createChainTx` | `CreateChainTx` | The create chain transaction instance | | `subnetOwners` | `PChainOwner` | The subnet owners | | `subnetAuth` | `number[]` | Array of indices from subnet's owners array | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const createChainTx = await walletClient.pChain.prepareCreateChainTxn({ subnetId: "2b175hLJhGdj3CzgXNUHXDPVY3wQo3y3VWqPjKpF5vK", vmId: "avm", chainName: "MyCustomChain", genesisData: { // Genesis configuration }, subnetAuth: [0, 1], }); ``` **Related:** - [prepareCreateSubnetTxn](#preparecreatesubnettxn) - Create subnet --- ## prepareConvertSubnetToL1Txn Prepare a transaction to convert a subnet to an L1 (Layer 1) blockchain. **Function Signature:** ```typescript function prepareConvertSubnetToL1Txn( params: PrepareConvertSubnetToL1TxnParameters ): Promise; interface PrepareConvertSubnetToL1TxnParameters { subnetId: string; blockchainId: string; managerContractAddress: string; validators: L1Validator[]; subnetAuth: readonly number[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface L1Validator { nodeId: string; nodePoP: { publicKey: string; proofOfPossession: string; }; weight: bigint; initialBalanceInAvax: bigint; remainingBalanceOwner: PChainOwnerJSON; deactivationOwner: PChainOwnerJSON; } interface PrepareConvertSubnetToL1TxnReturnType { tx: UnsignedTx; convertSubnetToL1Tx: ConvertSubnetToL1Tx; subnetOwners: PChainOwner; subnetAuth: number[]; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------------------ | ------------------- | -------- | -------------------------------------------------------------------------- | | `subnetId` | `string` | Yes | Subnet ID of the subnet to convert | | `blockchainId` | `string` | Yes | Blockchain ID of the L1 where validator manager contract is deployed | | `managerContractAddress` | `string` | Yes | Address of the validator manager contract | | `validators` | `L1Validator[]` | Yes | Initial set of L1 validators after conversion | | `subnetAuth` | `readonly number[]` | Yes | Array of indices from subnet's owners array who will sign this transaction | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **L1 Validator Object:** | Name | Type | Description | | --------------------------- | ----------------- | ------------------------------------------------------------------------ | | `nodeId` | `string` | Node ID of the validator | | `nodePoP.publicKey` | `string` | Public key of the validator | | `nodePoP.proofOfPossession` | `string` | Proof of possession of the public key | | `weight` | `bigint` | Weight of the validator on the L1 used during consensus participation | | `initialBalanceInAvax` | `bigint` | Initial balance in nano AVAX required for paying contiguous fee | | `remainingBalanceOwner` | `PChainOwnerJSON` | Owner information for remaining balance if validator is removed/disabled | | `deactivationOwner` | `PChainOwnerJSON` | Owner information which can remove or disable the validator | **Returns:** | Type | Description | | --------------------------------------- | --------------------------------------- | | `PrepareConvertSubnetToL1TxnReturnType` | Convert subnet to L1 transaction object | **Return Object:** | Property | Type | Description | | --------------------- | --------------------- | --------------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `convertSubnetToL1Tx` | `ConvertSubnetToL1Tx` | The convert subnet to L1 transaction instance | | `subnetOwners` | `PChainOwner` | The subnet owners | | `subnetAuth` | `number[]` | Array of indices from subnet's owners array | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const convertSubnetToL1Tx = await walletClient.pChain.prepareConvertSubnetToL1Txn({ subnetId: "2b175hLJhGdj3CzgXNUHXDPVY3wQo3y3VWqPjKpF5vK", blockchainId: "2oYMBNV4eNHyqk2fjjV5nVwDzxvbmovtDAOwPJCTc9wqg8k9t", managerContractAddress: "0x1234567890123456789012345678901234567890", validators: [ { nodeId: "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", nodePoP: { publicKey: "0x...", proofOfPossession: "0x...", }, weight: BigInt(1000000), initialBalanceInAvax: avaxToNanoAvax(1000), remainingBalanceOwner: { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], threshold: 1, }, deactivationOwner: { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], threshold: 1, }, }, ], subnetAuth: [0, 1], }); ``` --- ## prepareRegisterL1ValidatorTxn Prepare a transaction to register an L1 validator. **Function Signature:** ```typescript function prepareRegisterL1ValidatorTxn( params: PrepareRegisterL1ValidatorTxnParameters ): Promise; interface PrepareRegisterL1ValidatorTxnParameters { initialBalanceInAvax: bigint; blsSignature: string; message: string; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareRegisterL1ValidatorTxnReturnType { tx: UnsignedTx; registerL1ValidatorTx: RegisterL1ValidatorTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ---------------------- | ---------- | -------- | -------------------------------------------------------------------------------------------- | | `initialBalanceInAvax` | `bigint` | Yes | Initial balance in nano AVAX required for paying contiguous fee | | `blsSignature` | `string` | Yes | BLS signature of the validator | | `message` | `string` | Yes | Signed warp message hex with `AddressedCall` payload containing `RegisterL1ValidatorMessage` | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ----------------------------------------- | ---------------------------------------- | | `PrepareRegisterL1ValidatorTxnReturnType` | Register L1 validator transaction object | **Return Object:** | Property | Type | Description | | ----------------------- | ----------------------- | ---------------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `registerL1ValidatorTx` | `RegisterL1ValidatorTx` | The register L1 validator transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const registerL1ValidatorTx = await walletClient.pChain.prepareRegisterL1ValidatorTxn({ initialBalanceInAvax: avaxToNanoAvax(1000), blsSignature: "0x...", message: "0x...", }); ``` --- ## prepareDisableL1ValidatorTxn Prepare a transaction to disable an L1 validator. **Function Signature:** ```typescript function prepareDisableL1ValidatorTxn( params: PrepareDisableL1ValidatorTxnParameters ): Promise; interface PrepareDisableL1ValidatorTxnParameters { validationId: string; disableAuth: number[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareDisableL1ValidatorTxnReturnType { tx: UnsignedTx; disableL1ValidatorTx: DisableL1ValidatorTx; disableOwners: PChainOwner; disableAuth: number[]; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------- | -------- | ---------------------------------------------------------------------------------------- | | `validationId` | `string` | Yes | Validation ID of the L1 validator | | `disableAuth` | `number[]` | Yes | Array of indices from L1 validator's disable owners array who will sign this transaction | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ---------------------------------------- | --------------------------------------- | | `PrepareDisableL1ValidatorTxnReturnType` | Disable L1 validator transaction object | **Return Object:** | Property | Type | Description | | ---------------------- | ---------------------- | --------------------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `disableL1ValidatorTx` | `DisableL1ValidatorTx` | The disable L1 validator transaction instance | | `disableOwners` | `PChainOwner` | The disable owners | | `disableAuth` | `number[]` | Array of indices from disable owners array | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const disableL1ValidatorTx = await walletClient.pChain.prepareDisableL1ValidatorTxn({ validationId: "0x...", disableAuth: [0, 1], }); ``` --- ## prepareSetL1ValidatorWeightTxn Prepare a transaction to set the weight of an L1 validator. **Function Signature:** ```typescript function prepareSetL1ValidatorWeightTxn( params: PrepareSetL1ValidatorWeightTxnParameters ): Promise; interface PrepareSetL1ValidatorWeightTxnParameters { message: string; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareSetL1ValidatorWeightTxnReturnType { tx: UnsignedTx; setL1ValidatorWeightTx: SetL1ValidatorWeightTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------- | -------- | --------------------------------------------------------------------------------------------- | | `message` | `string` | Yes | Signed warp message hex with `AddressedCall` payload containing `SetL1ValidatorWeightMessage` | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ------------------------------------------ | ------------------------------------------ | | `PrepareSetL1ValidatorWeightTxnReturnType` | Set L1 validator weight transaction object | **Return Object:** | Property | Type | Description | | ------------------------ | ------------------------ | ------------------------------------------------ | | `tx` | `UnsignedTx` | The unsigned transaction | | `setL1ValidatorWeightTx` | `SetL1ValidatorWeightTx` | The set L1 validator weight transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const setL1ValidatorWeightTx = await walletClient.pChain.prepareSetL1ValidatorWeightTxn({ message: "0x...", }); ``` --- ## prepareIncreaseL1ValidatorBalanceTxn Prepare a transaction to increase the balance of an L1 validator. **Function Signature:** ```typescript function prepareIncreaseL1ValidatorBalanceTxn( params: PrepareIncreaseL1ValidatorBalanceTxnParameters ): Promise; interface PrepareIncreaseL1ValidatorBalanceTxnParameters { balanceInAvax: bigint; validationId: string; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareIncreaseL1ValidatorBalanceTxnReturnType { tx: UnsignedTx; increaseL1ValidatorBalanceTx: IncreaseL1ValidatorBalanceTx; chainAlias: "P"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------- | -------- | -------------------------------------------------------- | | `balanceInAvax` | `bigint` | Yes | Amount of AVAX to increase the balance by (in nano AVAX) | | `validationId` | `string` | Yes | Validation ID of the L1 validator | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ------------------------------------------------ | ------------------------------------------------ | | `PrepareIncreaseL1ValidatorBalanceTxnReturnType` | Increase L1 validator balance transaction object | **Return Object:** | Property | Type | Description | | ------------------------------ | ------------------------------ | ------------------------------------------------------ | | `tx` | `UnsignedTx` | The unsigned transaction | | `increaseL1ValidatorBalanceTx` | `IncreaseL1ValidatorBalanceTx` | The increase L1 validator balance transaction instance | | `chainAlias` | `"P"` | The chain alias | **Example:** ```typescript const increaseL1ValidatorBalanceTx = await walletClient.pChain.prepareIncreaseL1ValidatorBalanceTxn({ balanceInAvax: avaxToNanoAvax(500), validationId: "0x...", }); ``` --- ## Next Steps - **[Wallet Methods](./wallet)** - General wallet operations - **[X-Chain Wallet Methods](./x-chain-wallet)** - X-Chain transaction preparation - **[C-Chain Wallet Methods](./c-chain-wallet)** - C-Chain atomic transactions - **[Account Management](../accounts)** - Account types and management # Wallet Methods (/docs/tooling/avalanche-sdk/client/methods/wallet-methods/wallet) --- title: Wallet Methods description: Complete reference for Avalanche wallet operations --- ## Overview The Avalanche Wallet Client provides methods for sending transactions, signing messages and transactions, and managing accounts across all Avalanche chains (P-Chain, X-Chain, and C-Chain). This reference covers all wallet-specific methods available through the Avalanche Wallet Client SDK. **Access:** `walletClient` ## send Send tokens from the source chain to the destination chain. Automatically handles cross-chain transfers when needed. **Function Signature:** ```typescript function send(params: SendParameters): Promise; interface SendParameters { account?: AvalancheAccount; amount: bigint; to: Address | XPAddress; from?: Address | XPAddress; sourceChain?: "P" | "C"; destinationChain?: "P" | "C"; token?: "AVAX"; context?: Context; } interface SendReturnType { txHashes: TransactionDetails[]; } interface TransactionDetails { txHash: string; chainAlias: "P" | "C"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------------ | ---------------------- | -------- | ----------------------------------------------------- | | `account` | `AvalancheAccount` | No | Account to send from (uses client account if omitted) | | `amount` | `bigint` | Yes | Amount to send in wei | | `to` | `Address \| XPAddress` | Yes | Destination address | | `from` | `Address \| XPAddress` | No | Source address (defaults to account address) | | `sourceChain` | `"P" \| "C"` | No | Source chain (default: "C") | | `destinationChain` | `"P" \| "C"` | No | Destination chain (default: "C") | | `token` | `"AVAX"` | No | Token to send (only AVAX supported) | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ---------------- | ------------------------- | | `SendReturnType` | Transaction hashes object | **Return Object:** | Property | Type | Description | | ---------- | ---------------------- | ---------------------------- | | `txHashes` | `TransactionDetails[]` | Array of transaction details | **Transaction Details Object:** | Property | Type | Description | | ------------ | ------------ | ---------------------------------- | | `txHash` | `string` | The hash of the transaction | | `chainAlias` | `"P" \| "C"` | The chain alias of the transaction | **Example:** ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToWei, avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); // Send AVAX on C-Chain const result = await walletClient.send({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", amount: avaxToWei(0.001), destinationChain: "C", }); console.log("Transaction hash:", result.txHashes[0].txHash); // Send AVAX from C-Chain to P-Chain const crossChainResult = await walletClient.send({ to: "P-avax1example...", amount: avaxToWei(1), sourceChain: "C", destinationChain: "P", }); console.log("Transfer transactions:", crossChainResult.txHashes); ``` **Related:** - [waitForTxn](#waitfortxn) - Wait for transaction confirmation - [signXPMessage](#signxpmessage) - Sign messages --- ## getAccountPubKey Get the public key associated with the wallet account in both EVM and XP formats. **Function Signature:** ```typescript function getAccountPubKey(): Promise; interface GetAccountPubKeyReturnType { evm: string; xp: string; } ``` **Returns:** | Type | Description | | ---------------------------- | ------------------ | | `GetAccountPubKeyReturnType` | Public keys object | **Return Object:** | Property | Type | Description | | -------- | -------- | ------------------------ | | `evm` | `string` | Public key in EVM format | | `xp` | `string` | Public key in XP format | **Example:** ```typescript const pubKeys = await walletClient.getAccountPubKey(); console.log("EVM public key:", pubKeys.evm); console.log("XP public key:", pubKeys.xp); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmgetaccountpubkey) - [Account Management](../accounts) - Account types and management --- ## waitForTxn Wait for a transaction to be confirmed on the network. **Function Signature:** ```typescript function waitForTxn(params: WaitForTxnParameters): Promise; interface WaitForTxnParameters { txHash: string; chainAlias: "X" | "P" | "C"; sleepTime?: number; maxRetries?: number; } ``` **Parameters:** | Name | Type | Required | Description | | ------------ | ------------------- | -------- | ------------------------------------------------------------ | | `txHash` | `string` | Yes | Transaction hash | | `chainAlias` | `"X" \| "P" \| "C"` | Yes | Chain where transaction was submitted | | `sleepTime` | `number` | No | Time to sleep between retries in milliseconds (default: 300) | | `maxRetries` | `number` | No | Maximum number of retries (default: 10) | **Returns:** | Type | Description | | --------------- | ----------------------------------------------------------------------------------- | | `Promise` | Promise that resolves when transaction is confirmed or rejects if transaction fails | **Example:** ```typescript const txHash = "0x..."; try { await walletClient.waitForTxn({ txHash, chainAlias: "C", sleepTime: 500, // Wait 500ms between checks maxRetries: 20, // Check up to 20 times }); console.log("Transaction confirmed!"); } catch (error) { console.error("Transaction failed:", error); } ``` **Related:** - [send](#send) - Send transactions - [Client tx status methods](../public-methods) - Check transaction status manually --- ## sendXPTransaction Send a signed XP transaction to the network (X-Chain, P-Chain, or C-Chain). **Function Signature:** ```typescript function sendXPTransaction( params: SendXPTransactionParameters ): Promise; interface SendXPTransactionParameters { account?: AvalancheAccount | Address; tx: string | UnsignedTx; chainAlias: "X" | "P" | "C"; externalIndices?: number[]; internalIndices?: number[]; utxoIds?: string[]; feeTolerance?: number; subnetAuth?: number[]; subnetOwners?: PChainOwner; disableOwners?: PChainOwner; disableAuth?: number[]; } interface SendXPTransactionReturnType { txHash: string; chainAlias: "X" | "P" | "C"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ----------------------------- | -------- | ----------------------------------------------------- | | `account` | `AvalancheAccount \| Address` | No | Account to use for the transaction | | `tx` | `string \| UnsignedTx` | Yes | Transaction to send (hex string or UnsignedTx object) | | `chainAlias` | `"X" \| "P" \| "C"` | Yes | Target chain | | `externalIndices` | `number[]` | No | External indices to use for the transaction | | `internalIndices` | `number[]` | No | Internal indices to use for the transaction | | `utxoIds` | `string[]` | No | UTXO IDs to use for the transaction | | `feeTolerance` | `number` | No | Fee tolerance to use for the transaction | | `subnetAuth` | `number[]` | No | Subnet auth to use for the transaction | | `subnetOwners` | `PChainOwner` | No | Subnet owners to use for the transaction | | `disableOwners` | `PChainOwner` | No | Disable owners to use for the transaction | | `disableAuth` | `number[]` | No | Disable auth to use for the transaction | **Returns:** | Type | Description | | ----------------------------- | ----------------------- | | `SendXPTransactionReturnType` | Transaction hash object | **Return Object:** | Property | Type | Description | | ------------ | ------------------- | --------------------------- | | `txHash` | `string` | The hash of the transaction | | `chainAlias` | `"X" \| "P" \| "C"` | The chain alias | **Example:** ```typescript // This is typically used with prepare methods from chain-specific wallets const unsignedTx = await walletClient.pChain.prepareBaseTxn({ outputs: [ { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], amount: avaxToNanoAvax(1), }, ], }); const signedTx = await walletClient.signXPTransaction({ tx: unsignedTx.tx, chainAlias: "P", }); const { txHash } = await walletClient.sendXPTransaction({ tx: signedTx.signedTxHex, chainAlias: "P", }); console.log("Transaction hash:", txHash); ``` **Related:** - [signXPTransaction](#signxptransaction) - Sign transactions - [P-Chain Wallet Methods](./p-chain-wallet) - P-Chain transaction preparation - [X-Chain Wallet Methods](./x-chain-wallet) - X-Chain transaction preparation --- ## signXPTransaction Sign an XP transaction (X-Chain, P-Chain, or C-Chain). **Function Signature:** ```typescript function signXPTransaction( params: SignXPTransactionParameters ): Promise; interface SignXPTransactionParameters { account?: AvalancheAccount | Address; tx?: string | UnsignedTx; signedTxHex?: string; chainAlias: "X" | "P" | "C"; utxoIds?: string[]; subnetAuth?: number[]; subnetOwners?: PChainOwner; disableOwners?: PChainOwner; disableAuth?: number[]; context?: Context; } interface Signatures { signature: string; sigIndices: number[]; } interface SignXPTransactionReturnType { signedTxHex: string; signatures: Signatures[]; chainAlias: "X" | "P" | "C"; subnetAuth?: number[]; subnetOwners?: PChainOwner; disableOwners?: PChainOwner; disableAuth?: number[]; } ``` **Parameters:** | Name | Type | Required | Description | | --------------- | ----------------------------- | -------- | ---------------------------------------------------------------------------- | | `account` | `AvalancheAccount \| Address` | No | Account to use for the transaction | | `tx` | `string \| UnsignedTx` | No | Unsigned transaction (either `tx` or `signedTxHex` must be provided) | | `signedTxHex` | `string` | No | Pre-signed transaction bytes (either `tx` or `signedTxHex` must be provided) | | `chainAlias` | `"X" \| "P" \| "C"` | Yes | Target chain | | `utxoIds` | `string[]` | No | UTXO IDs to use for the transaction | | `subnetAuth` | `number[]` | No | Subnet auth to use for the transaction | | `subnetOwners` | `PChainOwner` | No | Subnet owners to use for the transaction | | `disableOwners` | `PChainOwner` | No | Disable owners to use for the transaction | | `disableAuth` | `number[]` | No | Disable auth to use for the transaction | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ----------------------------- | ------------------------- | | `SignXPTransactionReturnType` | Signed transaction object | **Return Object:** | Property | Type | Description | | --------------- | ------------------- | --------------------------------------- | | `signedTxHex` | `string` | The signed transaction in hex format | | `signatures` | `Signatures[]` | Array of signatures for the transaction | | `chainAlias` | `"X" \| "P" \| "C"` | The chain alias | | `subnetAuth` | `number[]?` | Subnet auth used for the transaction | | `subnetOwners` | `PChainOwner?` | Subnet owners used for the transaction | | `disableOwners` | `PChainOwner?` | Disable owners used for the transaction | | `disableAuth` | `number[]?` | Disable auth used for the transaction | **Signatures Object:** | Property | Type | Description | | ------------ | ---------- | ------------------------------------------------------------------------- | | `signature` | `string` | The signature of the transaction with the current account | | `sigIndices` | `number[]` | The indices of the signatures. Contains [inputIndex, signatureIndex] pair | **Example:** ```typescript const unsignedTx = await walletClient.pChain.prepareBaseTxn({ outputs: [ { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], amount: avaxToNanoAvax(0.1), }, ], }); const signedTx = await walletClient.signXPTransaction({ tx: unsignedTx.tx, chainAlias: "P", }); // Now send the signed transaction const { txHash } = await walletClient.sendXPTransaction({ tx: signedTx.signedTxHex, chainAlias: "P", }); console.log("Transaction hash:", txHash); ``` **Related:** - [sendXPTransaction](#sendxptransaction) - Send signed transaction - [signXPMessage](#signxpmessage) - Sign messages --- ## signXPMessage Sign a message with an XP account (P-Chain or X-Chain addresses). **Function Signature:** ```typescript function signXPMessage( params: SignXPMessageParameters ): Promise; interface SignXPMessageParameters { account?: AvalancheAccount | Address; message: string; accountIndex?: number; } interface SignXPMessageReturnType { signature: string; } ``` **Parameters:** | Name | Type | Required | Description | | -------------- | ----------------------------- | -------- | --------------------------------------------------------------------------------- | | `account` | `AvalancheAccount \| Address` | No | Account to use for the message | | `message` | `string` | Yes | Message to sign | | `accountIndex` | `number` | No | Account index to use for the message from custom transport (e.g., core extension) | **Returns:** | Type | Description | | ------------------------- | ------------------------ | | `SignXPMessageReturnType` | Message signature object | **Return Object:** | Property | Type | Description | | ----------- | -------- | ------------------------------------ | | `signature` | `string` | Hex-encoded signature of the message | **Example:** ```typescript const { signature } = await walletClient.signXPMessage({ message: "Hello Avalanche", }); console.log("Signature:", signature); ``` **Related:** - [API Reference](https://build.avax.network/docs/rpcs/x-chain#avmsignmessage) - [signXPTransaction](#signxptransaction) - Sign transactions --- ## EVM Transaction Methods The Avalanche Wallet Client extends viem's Wallet Client, providing access to all standard Ethereum transaction methods. ### Standard viem Methods ```typescript // Send transaction const hash = await walletClient.sendTransaction({ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", value: parseEther("0.001"), }); // Sign message const signature = await walletClient.signMessage({ message: "Hello World", }); // Sign typed data (EIP-712) const typedSignature = await walletClient.signTypedData({ domain: { ... }, types: { ... }, primaryType: "Message", message: { ... }, }); ``` **For complete EVM wallet method reference, see:** [Viem Documentation](https://viem.sh/docs) --- ## Chain-Specific Wallet Operations ### P-Chain Wallet Operations Access through `walletClient.pChain`: ```typescript // Prepare base transaction const baseTx = await walletClient.pChain.prepareBaseTxn({ outputs: [ { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], amount: avaxToNanoAvax(1), }, ], }); // Prepare export transaction const exportTx = await walletClient.pChain.prepareExportTxn({ destinationChain: "C", exportedOutputs: [ { addresses: ["0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6"], amount: avaxToNanoAvax(1), }, ], }); // Prepare import transaction const importTx = await walletClient.pChain.prepareImportTxn({ sourceChain: "C", importedOutput: { addresses: ["P-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], }, }); ``` See [P-Chain Wallet Methods](./p-chain-wallet) for complete reference. ### X-Chain Wallet Operations Access through `walletClient.xChain`: ```typescript // Prepare base transaction const baseTx = await walletClient.xChain.prepareBaseTxn({ outputs: [ { addresses: ["X-fuji19fc97zn3mzmwr827j4d3n45refkksgms4y2yzz"], amount: avaxToNanoAvax(1), }, ], }); ``` See [X-Chain Wallet Methods](./x-chain-wallet) for complete reference. ### C-Chain Wallet Operations Access through `walletClient.cChain`: ```typescript // Prepare export transaction const exportTx = await walletClient.cChain.prepareExportTxn({ destinationChain: "P", fromAddress: account.getEVMAddress(), exportedOutput: { addresses: [account.getXPAddress("P")], amount: avaxToNanoAvax(1), }, }); // Prepare import transaction const importTx = await walletClient.cChain.prepareImportTxn({ sourceChain: "P", toAddress: account.getEVMAddress(), }); ``` See [C-Chain Wallet Methods](./c-chain-wallet) for complete reference. --- ## Next Steps - **[P-Chain Wallet Methods](./p-chain-wallet)** - P-Chain transaction preparation - **[X-Chain Wallet Methods](./x-chain-wallet)** - X-Chain transaction preparation - **[C-Chain Wallet Methods](./c-chain-wallet)** - C-Chain atomic transactions - **[Account Management](../accounts)** - Account types and creation - **[Viem Documentation](https://viem.sh/docs)** - Complete EVM wallet reference # X-Chain Wallet Methods (/docs/tooling/avalanche-sdk/client/methods/wallet-methods/x-chain-wallet) --- title: X-Chain Wallet Methods description: Complete reference for X-Chain transaction preparation methods --- ## Overview The X-Chain Wallet Methods provide transaction preparation capabilities for the Exchange Chain. These methods allow you to create unsigned transactions for various operations including base transfers and cross-chain transfers. **Access:** `walletClient.xChain` ## prepareBaseTxn Prepare a base X-Chain transaction for transferring AVAX or other assets. **Function Signature:** ```typescript function prepareBaseTxn( params: PrepareBaseTxnParameters ): Promise; interface PrepareBaseTxnParameters { outputs?: Output[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface Output { addresses: string[]; amount: bigint; assetId?: string; locktime?: bigint; threshold?: number; } interface PrepareBaseTxnReturnType { tx: UnsignedTx; baseTx: BaseTx; chainAlias: "X"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------- | -------- | --------------------------------------------- | | `outputs` | `Output[]` | No | Array of outputs to send funds to | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Output Object:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ------------------------------------------------------------------ | | `addresses` | `string[]` | Yes | Addresses who can sign the consuming of this UTXO | | `amount` | `bigint` | Yes | Amount in nano AVAX | | `assetId` | `string` | No | Asset ID of the UTXO | | `locktime` | `bigint` | No | Timestamp in seconds after which this UTXO can be consumed | | `threshold` | `number` | No | Threshold of `addresses`' signatures required to consume this UTXO | **Returns:** | Type | Description | | -------------------------- | ----------------------- | | `PrepareBaseTxnReturnType` | Base transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ----------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `baseTx` | `BaseTx` | The base transaction instance | | `chainAlias` | `"X"` | The chain alias | **Example:** ```typescript import { createAvalancheWalletClient } from "@avalanche-sdk/client"; import { privateKeyToAvalancheAccount } from "@avalanche-sdk/client/accounts"; import { avalanche } from "@avalanche-sdk/client/chains"; import { avaxToNanoAvax } from "@avalanche-sdk/client/utils"; const account = privateKeyToAvalancheAccount("0x..."); const walletClient = createAvalancheWalletClient({ account, chain: avalanche, transport: { type: "http" }, }); const unsignedTx = await walletClient.xChain.prepareBaseTxn({ outputs: [ { addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], amount: avaxToNanoAvax(1), }, ], }); // Sign and send const signedTx = await walletClient.signXPTransaction({ tx: unsignedTx.tx, chainAlias: "X", }); const { txHash } = await walletClient.sendXPTransaction({ tx: signedTx.signedTxHex, chainAlias: "X", }); console.log("Transaction hash:", txHash); ``` **Related:** - [prepareExportTxn](#prepareexporttxn) - Cross-chain exports - [prepareImportTxn](#prepareimporttxn) - Cross-chain imports --- ## prepareExportTxn Prepare a transaction to export AVAX or other assets from X-Chain to another chain. **Function Signature:** ```typescript function prepareExportTxn( params: PrepareExportTxnParameters ): Promise; interface PrepareExportTxnParameters { destinationChain: "P" | "C"; exportedOutputs: Output[]; fromAddresses?: string[]; changeAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface PrepareExportTxnReturnType { tx: UnsignedTx; exportTx: ExportTx; chainAlias: "X"; } ``` **Parameters:** | Name | Type | Required | Description | | ------------------ | ------------ | -------- | --------------------------------------------- | | `destinationChain` | `"P" \| "C"` | Yes | Chain alias to export funds to | | `exportedOutputs` | `Output[]` | Yes | Outputs to export | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `changeAddresses` | `string[]` | No | Addresses to receive change | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Returns:** | Type | Description | | ---------------------------- | ------------------------- | | `PrepareExportTxnReturnType` | Export transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `exportTx` | `ExportTx` | The export transaction instance | | `chainAlias` | `"X"` | The chain alias | **Example:** ```typescript const exportTx = await walletClient.xChain.prepareExportTxn({ destinationChain: "C", exportedOutputs: [ { addresses: ["0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6"], amount: avaxToNanoAvax(1), }, ], }); // Sign and send const signedTx = await walletClient.signXPTransaction({ tx: exportTx.tx, chainAlias: "X", }); const { txHash } = await walletClient.sendXPTransaction({ tx: signedTx.signedTxHex, chainAlias: "X", }); ``` **Related:** - [prepareImportTxn](#prepareimporttxn) - Import to X-Chain - [Wallet send method](./wallet#send) - Simplified cross-chain transfers --- ## prepareImportTxn Prepare a transaction to import AVAX or other assets from another chain to X-Chain. **Function Signature:** ```typescript function prepareImportTxn( params: PrepareImportTxnParameters ): Promise; interface PrepareImportTxnParameters { sourceChain: "P" | "C"; importedOutput: ImportedOutput; fromAddresses?: string[]; utxos?: Utxo[]; memo?: string; minIssuanceTime?: bigint; context?: Context; } interface ImportedOutput { addresses: string[]; locktime?: bigint; threshold?: number; } interface PrepareImportTxnReturnType { tx: UnsignedTx; importTx: ImportTx; chainAlias: "X"; } ``` **Parameters:** | Name | Type | Required | Description | | ----------------- | ---------------- | -------- | ----------------------------------------------- | | `sourceChain` | `"P" \| "C"` | Yes | Chain alias to import funds from | | `importedOutput` | `ImportedOutput` | Yes | Consolidated imported output from atomic memory | | `fromAddresses` | `string[]` | No | Addresses to send funds from | | `utxos` | `Utxo[]` | No | UTXOs to use as inputs | | `memo` | `string` | No | Transaction memo | | `minIssuanceTime` | `bigint` | No | Earliest time this transaction can be issued | | `context` | `Context` | No | Transaction context (auto-fetched if omitted) | **Imported Output Object:** | Name | Type | Required | Description | | ----------- | ---------- | -------- | ----------------------------------------------------------------------------------- | | `addresses` | `string[]` | Yes | Addresses who can sign the consuming of this UTXO | | `locktime` | `bigint` | No | Timestamp in seconds after which this UTXO can be consumed | | `threshold` | `number` | No | Number of signatures required out of total `addresses` to spend the imported output | **Returns:** | Type | Description | | ---------------------------- | ------------------------- | | `PrepareImportTxnReturnType` | Import transaction object | **Return Object:** | Property | Type | Description | | ------------ | ------------ | ------------------------------- | | `tx` | `UnsignedTx` | The unsigned transaction | | `importTx` | `ImportTx` | The import transaction instance | | `chainAlias` | `"X"` | The chain alias | **Example:** ```typescript const importTx = await walletClient.xChain.prepareImportTxn({ sourceChain: "C", importedOutput: { addresses: ["X-avax18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"], threshold: 1, }, }); // Sign and send const signedTx = await walletClient.signXPTransaction({ tx: importTx.tx, chainAlias: "X", }); const { txHash } = await walletClient.sendXPTransaction({ tx: signedTx.signedTxHex, chainAlias: "X", }); ``` **Related:** - [prepareExportTxn](#prepareexporttxn) - Export from X-Chain --- ## Next Steps - **[Wallet Methods](./wallet)** - General wallet operations - **[P-Chain Wallet Methods](./p-chain-wallet)** - P-Chain transaction preparation - **[C-Chain Wallet Methods](./c-chain-wallet)** - C-Chain atomic transactions - **[Account Management](../accounts)** - Account types and management # Welcome to the Course (/academy/avalanche-l1/avacloudapis) --- title: Welcome to the Course description: Learn about AvaCloud APIs and the AvaCloud SDK. updated: 2024-09-03 authors: [owenwahlgren] icon: Smile --- ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-banner/avacloudapis-ThOanH9UQJiAizv9gKtgFufXxRywkQ.jpg) ## Why Take This Course? [AvaCloud APIs](https://developers.avacloud.io/introduction), built by [AvaCloud](https://avacloud.io/), provides Web3 developers with multi-chain data from Avalanche’s primary network and other Avalanche L1s. With the AvaCloud API, you can easily build products that utilize real-time and historical transaction data, transfer records, native and token balances, and various types of token metadata. ## Course Content - [AvaCloud API Overview](/academy/avacloudapis/02-overview/01-about) - [Environment Setup](/academy/avacloudapis/03-environment-setup/01-avacloud-account) - [Build an ERC-20 Token Balance App](/academy/avacloudapis/04-erc20-token-balance-app/01-overview) - [Build a Wallet Portfolio App](/academy/avacloudapis/05-wallet-portfolio-app/01-overview) - [Build a Basic Block Explorer](/academy/avacloudapis/06-block-explorer-app/01-overview) ## Prerequisites A general understanding of web development is required. While you won't need to write a lot of code, familiarity with key concepts is important. - **TypeScript:** Understanding of common concepts, as all exercises will use TypeScript. - **React:** Basic understanding of React is required, as all exercises will use React. - **NextJS:** Basic understanding of NextJS is required, as all exercises will use NextJS. ## Learning Outcomes By the end of this course, you will: - Be familiar with the AvaCloud API including the [Data API](https://developers.avacloud.io/data-api/overview) and [Webhooks API](https://developers.avacloud.io/webhooks-api/overview). - Learn how to use the [AvaCloudSDK](https://github.com/ava-labs/avacloud-sdk-typescript) to interact with [AvaCloud APIs](https://developers.avacloud.io/introduction). - Build multiple web apps using the [AvaCloudSDK](https://github.com/ava-labs/avacloud-sdk-typescript). # Course Completion Certificate (/academy/avalanche-l1/avalanche-fundamentals/get-certificate) --- title: Course Completion Certificate updated: 2024-09-09 authors: [ashucoder9] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course. Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the Course (/academy/avalanche-l1/avalanche-fundamentals) --- title: Welcome to the Course description: Learn about the basics of Avalanche. updated: 2024-05-31 authors: [ashucoder9] icon: Smile --- Welcome to the Avalanche Fundamentals, an online course introducing you to the exciting world of the Avalanche technology! This course will provide you with a comprehensive understanding of the basic concepts making Avalanche unique. Throughout, you'll learn the key features and benefits of the platform, plus how to build on it. You can also ask our expert instructors questions. By the end of these courses, you'll have the knowledge and skills to leverage the power of blockchain technology for your own projects and applications. We're excited to have you join us on this journey and can't wait to see what you'll create with Avalanche! ## Prerequisites This course is for people with some blockchain knowledge. Check out this [guide](/guides/what-is-a-blockchain) to review what a blockchain is and this [blog](/blog/what-is-fuji-testnet) to understand why and how testnets work. Familiarity with the basic design of modern distributed software systems and common blockchain systems such as Bitcoin and Ethereum is also recommended. You do not need to know how to write code to successfully complete this course. Having these prerequisites will help you better understand the course material and engage in the activities. If you don't know whether you have the necessary foundation for this course, please contact the course instructor for guidance. ## Learning Outcomes By the end of this course, you will: - Understand how Avalanche consensus works and what makes it different. - Understand how Avalanche L1s enable scalability, customizability, and independence. - Understand the Primary Network, a special Avalanche L1, and how to interact with it. - Understand how Virtual Machines enable developers to create more optimized and capable blockchain systems and to tackle completely new use cases unachievable with previous solutions. You can evaluate your own understanding of the material through quizzes and claim a certificate for successful completion at the end. Overall, this course aims to provide a foundational understanding of Avalanche. By completing it, you will be better prepared to take on more advanced courses focused on building on Avalanche. # Welcome to the Course (/academy/avalanche-l1/customizing-evm) --- title: Welcome to the Course description: Learn how to customize the Ethereum Virtual Machine. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: Smile --- import { Step, Steps } from 'fumadocs-ui/components/steps'; ## Why take this Course? A significant innovation in blockchain is the development of multi-chain systems, such as Avalanche, which provide a significant improvement in scalability, interoperability, and flexibility. At the core of these multi-chain systems is the ability to run multiple blockchains powered by different virtual machines simultaneously. Each VM of a chain is optimized for specialized use cases, thereby boosting the network's overall performance. Configuring and modifying the EVM is an efficient way to create a specialized virtual machine, as it allows developers to build upon years of active community work and leverage the extensive ecosystem surrounding the EVM, including wallets, explorers, and development frameworks. ## Course Content ### EVM Basics & Precompiles In the first section of the course we will go through some basic concepts of the EVM, such as the account-based model, keys, addresses, transactions, and blocks. Furthermore, we will explain what a precompile is and show you how to interact with a precompile on the Fuji testnet. ### Development Environment Setup We will explore various ways to set up a development environment for building customized EVMs. You'll learn about GitHub Codespaces and Development Containers (`.devcontainer`). If you choose a local setup, you'll install a code editor, the Go language, configure your shell, and install additional dependencies. ### Hands-On Exercises The following sections contain hands-on exercises where you customize the EVM. The difficulty of the customizations will increase from section to section. Find out more about each exercise by clicking its name below: - Learn how the Avalanche Network Runner Works - Create a your own Avalanche Network - Create an EVM Blockchain on your network - Connect Core Wallet to your blockchain - Learn what the genesis block is and how the data is structured - Create a custom gas fee structure for your blockchain - Define the initial token allocation at launch - Configure pre-installed precompiles - Create the blockchain and connect to it Learn about the basic building blocks of a precompile by building a precompile for the md5 hash function: - Generate boilerplate code for the precompile from a solidity interface - Learn how to unpack inputs and pack outputs into 32 byte arrays - Configure and register your md5 precompile - Create a new blockchain, connect to it and interact with your precompile Learn to build more complex precompiles by building a calculator precompile and master the following skills in addition to the previous section: - Unpack multiple inputs and pack multiple outputs into 32 byte arrays - Set a gas cost for your precompile - Add go tests for your precompile Learn to build stateful precompiles by building a counter precompile and master the following skills in addition to the previous sections: - Read from and write to the EVM state - Define the initial state in the precompileConfig - Add solidity tests for your precompile Learn to build permissioned precompiles by building a XXX precompile and master the following skills in addition to the previous sections: - Add permissions to only allow certain addresses to interact with the precompile - Define the initial permissions in the precompileConfig - Change the permissions on the fly ## Prerequisites ### Avalanche This course is intended for people with a solid understanding of the basic concepts of Avalanche. You should be familiar with these concepts: 1. **Virtual Machines**: What they are and what VM customization means 2. **Blockchains**: What the components are of a blockchain and how they interact, specifically how Avalanche L1s leverage precompiles. If some of this is not clear, I strongly recommend taking the [Avalanche Fundamentals](/academy/avalanche-l1/avalanche-fundamentals) and [Multi-Chain Architecture](/academy/multi-chain-architecture) courses first. ### Coding You will need a general understanding of software development. You won't have to write a lot of code, but you will have to be able to understand some. Therefore, we recommend: 1. **Solidity**: Basic knowledge, familiarity with types and interfaces. Familiarity with Foundry will help in advanced sections. 2. **Go**: You don't necessarily need to know Go, but should have some experience with an advanced object-oriented and typed language (C, C++, Java, Typescript) 3. **Testing**: It will help you in later sections if you are generally familiar with the concept of unit testing ## Learning Outcomes By the end of this course, students will be able to: - Understand what Precompiles are and when to use them. - Understand how developing precompiles allows developers to create more optimized and capable blockchain systems, enabling them to address entirely new use cases that were previously unattainable. - Apply the knowledge gained in the course by building multiple precompiles ![](/wolfie/wolfie-hack.png) # Welcome to the Course (/academy/avalanche-l1/erc20-bridge) --- title: Welcome to the Course description: Learn how to bridge ERC-20 tokens between Avalanche L1s using Interchain Token Transfer. updated: 2025-01-24 authors: [nicolasarnedo] icon: Smile --- ## ERC-20 to ERC-20 Bridge Welcome to the **ERC-20 to ERC-20 Bridge** course! This course is designed to give you a deep understanding of bridging ERC-20 tokens between Avalanche L1s using Avalanche Interchain Token Transfer (ICTT). By the end of this course, you will have practical skills in deploying token bridges, understanding bridge security, and implementing multi-chain token transfers using Avalanche's native interoperability features. ### Prerequisites Before starting this course, we recommend completing: - [Interchain Messaging](/academy/avalanche-l1/interchain-messaging) - Foundation in cross-chain communication with ICM - [L1 Native Tokenomics](/academy/avalanche-l1/l1-native-tokenomics) - Understanding tokens fundamentals ### What You'll Learn This comprehensive course will walk you through: - **Token Bridging Fundamentals** - Understanding bridge architecture, security considerations, and common bridge vulnerabilities - **Avalanche Interchain Token Transfer** - Learning the ICTT protocol design, TokenHome and TokenRemote contracts - **ERC-20 to ERC-20 Bridge** - Deploying your own token bridge, registering remote chains, and transferring tokens - **Tokens on Multiple Chains** - Managing tokens across multiple L1s and implementing multi-hop transfers ### Let's Get Started! Each section builds upon previous knowledge, so we recommend completing the course in order. Happy learning and building! # Course Completion Certificate (/academy/avalanche-l1/icm-chainlink/certificate) --- title: Course Completion Certificate updated: 2024-10-11 authors: [owenwahlgren] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course. Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the course (/academy/avalanche-l1/icm-chainlink) --- title: Welcome to the course description: Enable Chainlink services on L1 networks that do not have direct Chainlink support. updated: 2024-05-31 authors: [martineckardt] icon: Smile --- In this course, you will learn how to integrate your own blockchain with the Chainlink services that are deployed on C-chain. To do this, we will use Avalanche Interchain Messaging and Chainlink VRF. ## Why Take This Course? As the blockchain ecosystem grows, developers require robust tools to build secure, scalable, and innovative applications. Chainlink provides a suite of decentralized services, including VRF (Verifiable Random Function), Automation, Chainlink Functions, and CCIP (Cross-Chain Interoperability Protocol), to empower developers in creating high-performance, trustless applications. These services are not integrated by default on Avalanche L1 chains. However, Avalanche’s Interchain Messaging (ICM) enables developers to consume Chainlink services, typically available on the C-Chain, directly within their own L1 environments. This course equips you with the knowledge to integrate all Chainlink services into your Avalanche L1 blockchain. You will start with Chainlink VRF and progressively expand to advanced features like Automation for task scheduling, Functions for off-chain data computations, and CCIP for bridging data between Ethereum and Avalanche. We suggest you to revisit this course as we will keep on adding new content. ## Course Content ### Introduction Overview of Chainlink services and their role in decentralized applications. Importance of integrating these tools into Avalanche L1 chains. ### Chainlink VRF Explanation of VRF and its use cases. Deploying VRF on Avalanche L1 and verifying randomness. ### Chainlink Functions Overview of off-chain computation and API integrations. Implementing Functions for real-time data feeds. ### Chainlink Automation Using Automation for task scheduling. Examples include automated smart contracts and dynamic pricing. ### Chainlink CCIP Bridging data and assets between Ethereum and Avalanche L1. Practical applications such as cross-chain governance and token transfers. ## Prerequisites Avalanche Interchain Messaging (ICM): A solid understanding of Avalanche’s ICM protocol is essential. You should have completed the ICM course, which covers: - The fundamentals of cross-chain communication in Avalanche - Message formats and flows - Security techniques for interchain messaging Blockchain and Development Knowledge - Solidity: Familiarity with the language’s key concepts, especially those related to smart contract interactions and randomness. - Oracles: While we will cover the basic components of Chainlink's VRF, having a sense of oracles is desirable. If any of this is unclear, we strongly recommend taking the Avalanche Interchain Messaging course first. ## Learning Outcomes By the end of this course, students will: - Understand the mechanics of Chainlink VRF and other Chainlink Services and its significance in decentralized applications. - Gain proficiency in setting up Avalanche L1 chains with Chainlink services enabled. - Integrate Chainlink VRF with Avalanche ICM for secure cross-chain randomness. - Use Chainlink functions for Off-chain data integration with on-chain logic. - Automated smart contract operations - Cross-Chain communication with ecosystems outside Avalanche trough Chainlink's CCIP - Apply best practices to ensure reliability, security, and cost-efficiency in their applications. This course prepares developers to leverage Chainlink’s powerful suite of tools for building next-generation blockchain solutions on Avalanche L1. # Course Completion Certificate (/academy/avalanche-l1/interchain-token-transfer/certificate) --- title: Course Completion Certificate updated: 2024-10-11 authors: [owenwahlgren] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course. Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the Course (/academy/avalanche-l1/interchain-token-transfer) --- title: Welcome to the Course description: Learn about sending assets to another L1s with Avalanche Interchain Token Transfer. updated: 2024-05-31 authors: [ashucoder9] icon: Smile --- In this course, you will learn how to transfer assets across multiple Avalanche blockchains with Avalanche Interchain Token Transfer ICTT. ## Why Take This Course? A significant innovation in blockchain is the development of multi-chain systems, like Avalanche, which provide a significant improvement in scalability, interoperability, and flexibility. At the core of these multi-chain systems is the ability to run multiple blockchains that communicate. Each chain's VM is optimized for specialized use cases, thereby boosting the network's overall performance. Cross-chain communication is a crucial building block of multi-chain systems. Utilizing Avalanche Interchain Messaging and Interchain Token Transfer is an incredibly easy way to build cross-Avalanche L1 dApps, since developers can build on top of an extensive, audited development framework. Transfering tokens between multiple chains is a common use case in multi-chain systems. This course will help you understand how to transfer assets between multiple Avalanche blockchains using the Avalanche Interchain Token Transfer protocol. This course focuses on using the existing testnet chains (Fuji C-Chain, Echo, and Dispatch). If you want to create your own L1 blockchain, please refer to the [Creating an L1](/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/01-creating-an-l1) course. Since Interchain Token Transfer relies on Interchain Messaging (ICM), you must run your own relayer to enable cross-chain communication. Follow the [Running a Relayer](/academy/interchain-messaging/09-running-a-relayer/01-relayer-introduction) course to set up your relayer. ## Course Content ### Getting Started with Interchain Token Transfer In this section, you will learn how to use our Interchain Token Transfer toolbox to perform cross-chain operations. We'll guide you through the process of using our user-friendly interface to deploy contracts, create bridges, and transfer assets across the testnet chains (Fuji C-Chain, Echo, and Dispatch). ### Tokens and Token Types In this section, you will learn about the different types of tokens that can be transferred between Avalanche blockchains. We will cover ERC-20 and native tokens and how to deploy and transfer them using our toolbox. Furthermore, you will learn what wrapped native tokens are and how they can be used to transfer assets between chains. ### Token Bridging Next we will talk about the high level concepts of token bridging and demonstrate how to use our toolbox to create and manage bridge contracts for cross-chain transfers between the testnet chains. ### Interchain Token Transfer Architecture In this chapter we will look at the design of Avalanche Interchain Token Transfer. You will learn about the file structure of the contracts and the concepts of the token home and token remote. ### ERC-20 to ERC-20 Bridge Implementation You will learn how to use our toolbox to deploy ERC-20 tokens and create bridges to transfer them between the testnet chains. ### Multi-Chain Token Operations Here you will learn about the concept of multi-hops and how to use our toolbox to bridge tokens between multiple testnet chains. ### Native to ERC-20 Bridge Implementation In this chapter you will learn how to use our toolbox to bridge a native token as an ERC-20 token to another testnet chain. ### Send and Call Operations In this chapter you will learn how to use our toolbox to call smart contracts with the tokens after sending them to another testnet chain. ### Cross-Chain Token Swaps In this chapter you will learn how to perform cross-chain token swaps between the testnet chains using our toolbox. ## Prerequisites ### Avalanche Knowledge This course is intended for people with knowledge about Cross-Chain communication protocols, and a solid understanding of the basic concepts of Avalanche. You should be familiar with these concepts: 1. Avalanche Architecture: Be familiar with Avalanche blockchains. 2. Interchain Messaging: Know how to communicate between two Avalanche blockchains with ICM. If some of this is not clear, we strongly recommend taking the Avalanche Fundamentals, Multi-Chain Architecture, and Interchain Messaging courses first. ### Software Development You will need a general understanding of how to use Web3 applications. We recommend: 1. Basic understanding of how to use Core Wallet - Download from [core.app](https://core.app) 2. Test tokens for development - **Recommended:** Create a [Builder Hub account](https://build.avax.network/login) and connect your wallet to receive testnet tokens automatically - **Alternative:** Use external faucets like [core.app/tools/testnet-faucet](https://core.app/tools/testnet-faucet/?subnet=c&token=c) with code `avalanche-academy` 3. Understanding of token standards (ERC-20, etc.) ## Learning Outcomes By the end of this course, you will: - Understand what Avalanche Interchain Token Transfer is and when to use it. - Understand the different options for transferring assets between multiple chains. - Be able to deploy tokens and create bridges using our toolbox. - Be able to perform cross-chain token transfers between testnet chains using our toolbox. - Apply the knowledge gained in the course by enabling assets to be transferred between multiple Avalanche blockchains. # Course Completion Certificate (/academy/avalanche-l1/interchain-messaging/certificate) --- title: Course Completion Certificate updated: 2024-10-11 authors: [owenwahlgren] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course. Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the course (/academy/avalanche-l1/interchain-messaging) --- title: Welcome to the course description: Learn about Interchain Messaging, the interoperability protocol of Avalanche. updated: 2025-05-13 authors: [martineckardt, nicolasarnedo] icon: Smile --- In this course, you will learn how to build cross-L1 Solidity dApps with Interchain Messaging and Avalanche Warp Messaging. ## Why Take This Course? A significant innovation in blockchain is the development of multi-chain systems, like Avalanche, which provide a significant improvement in scalability, interoperability, and flexibility. At the core of these multi-chain systems is the ability to run multiple blockchains that communicate. Each chain's VM is optimized for specialized use cases, thereby boosting the network's overall performance. Cross-chain communication is a crucial building block of multi-chain systems. Utilizing Interchain Messaging and Avalanche Warp Messaging is an incredible easy way to build cross-L1 dApps, since developers can build on top an extensive and audited development framework. ## Course Content Below you can find a 30 minute recording of a presentation about Avalanche Warp Messaging and Teleporter. This summarizes the content of the first chapters: ### Interoperability In the first section, we cover some basic concepts of interoperability in multi-chain systems. You will learn about examples of interoperability between blockchains and the terms "source," "destination," and "message." ### Avalanche Interchain Messaging In this section, we learn what Avalanche Interchain Messaging is and what is abstracted away from the general dApp developer. You will also build your first cross-L1 dApps. ### Securing Cross-Chain Communication In this section, we look at techniques to secure cross-chain communication. We dive into signature schemes, multi-signature schemes, and the BLS multi-signature scheme. ### Avalanche Interchain Messaging Protocol Avalanche blockchains can natively interoperate between one another using AWM. You will learn about the AWM message format and how the message flow works. ## Prerequisites ### Avalanche This course is meant for people with a solid understanding of the basic concepts of Avalanche. You should be familiar with these concepts: - **Virtual Machines:** What they are and what VM customization means - **Avalanche L1s & Blockchains:** What the difference between a VM, Blockchain, and an Avalanche L1 is If any of this is unclear, we strongly recommend taking the Avalanche Fundamentals and Multi-Chain Architecture courses first. ### Software Development You will need a general understanding of Software Development. You won't have to write a lot of code, but you will have to understand some. Therefore, we recommend: - **Solidity:** Familiarity with most concepts of the language. All exercises will mainly consist of writing cross-subnet dApps in Solidity. - **Docker:** The advanced exercises in the latter part of the course will occur in contained environments for easy setup. It will help if you're generally familiar with the concept of containerization, docker, and docker compose. - **Testing:** Having some experience or familiarity with unit testing is ideal. ## Learning Outcomes By the end of this course, students will: - Understand the challenges of cross-chain communication - Know what separates Avalanche Warp Messaging from other cross-chain communication protocols - Understand the differences between Avalanche Warp Messaging and Teleporter - Apply their knowledge by building cross-Avalanche L1 dApps, such as asset bridges Overall, this course aims to provide an advanced understanding of Teleporter. By completing this course, students will be better prepared to build advanced cross-Avalanche L1 blockchain applications. # Dapp's and L1's (/academy/avalanche-l1/l1-native-tokenomics/dappVsL1) --- title: Dapp's and L1's description: Listing tokenomics and infrastructure requirements to understand whether creating an L1 is neccessary for your business-case updated: 2025-08-21 authors: [nicolasarnedo] icon: Microscope --- So far you have learnt a lot of the basics around blockchains, how they work, architecture, consensus mechanism's, smart contract's, and also Avalanche-specific concepts. Before reading through this course however, now is a good moment to reflect on whether your use-case requires it's own L1 and native tokenomics. In order to do this, there are several economic factors which will dictate if we should deploy an application on a public blockchain (Avalanche C-chain, Ethereum) or create our own custom Layer 1 (L1) blockchain. Let's look into them... ### Transaction Volume and Value If your application processes a high volume of low-value transactions, a custom L1 may be more cost-effective in the long run. Conversely, if your application handles only a few high-value transactions, the cost savings may not justify the complexity of running a separate L1. ### Fee Stability On a public blockchain, you are at the mercy of network-wide fee fluctuations. With a custom L1, you can ensure fee stability, which is crucial for budgeting and long-term planning. ### Data Control and Customization You can control who interacts with your application equally when deploying an app to a public chain vs on your own L1, but if you want to have more control on the data; who can deploy smart contracts, isolating your data for compliance/KYC reasons, or who can validate transactions, then you will absolutely need an L1. If you do have to create an L1 - the primary cost of running it is the infrastructure required to support the validator set. Once you establish an appropriate number of validators based on your security requirements, the operational costs become mostly fixed. These costs are independent of the number and complexity of the transactions processed on your chain, providing predictable financial outlays as your application scales. ### Token Location Choose based on where your security and token economics must live. If your token must be native to the protocol (gas token, staking/slashing, fee burn/distribution, protocol‑level issuance or supply rules) and you need validator/permissioning guarantees, a custom L1 is appropriate. If your token’s utility is application‑level (access, rewards, governance without base‑fee mechanics) and you benefit from public‑chain security and liquidity, a dApp with an ERC‑20 on a public chain is the simpler path. Decide whether the required guarantees are protocol‑layer (L1) or app‑layer (public chain). Launching a blockchain application on a public permissionless blockchain or spinning up your own L1 on Avalanche is a decision that depends heavily on your application's transaction profile and economic model. Ultimately, the decision should be driven by a careful analysis of your transaction patterns, user experience goals, what will be the token utilty and the potential for fee volatility in public networks. By weighing these factors, you can make an informed decision that aligns with your application's long-term success. #### *Use-case Example* *Gaming application where users frequently make micro-transactions, high transaction fees on a public blockchain could be a significant barrier to user retention. By deploying your own L1, you can minimize or even eliminate these fees, thereby enhancing the user experience and driving higher engagement. This is particularly relevant for applications that target mainstream audiences, where a seamless and cost-effective user experience is paramount.* **Live Proof**: [Off The Grid](https://gunzillagames.com/en/) uses a dedicated Avalanche L1 to power a player‑driven economy with on‑chain item ownership (NFTs) and currency ($GUNZ). # Welcome to the Course (/academy/avalanche-l1/l1-native-tokenomics) --- title: Welcome to the Course description: Learn about the L1 Native Tokenomics updated: 2025-08-21 authors: [nicolasarnedo] icon: Smile --- Welcome to the **L1 Native Tokenomics** course! This course is designed to give you a deep understanding of how to create and manage native tokens on your own Avalanche L1 blockchain. By the end of this course, you will have practical skills in designing tokenomics, configuring native token allocation, and leveraging precompiles to create powerful token economies. ## Prerequisites Before starting this course, you should have completed the [Blockchain Fundamentals](/academy/blockchain-fundamentals) and [Avalanche Fundamentals](/academy/avalanche-l1/avalanche-fundamentals) courses of the [Avalanche Developers Academy](https://build.avax.network/academy#:~:text=Choose%20Your%20Learning%20Path) learning tree. ## Learning Outcomes By the end of this course, you will: - **Understand Token Fundamentals**: Gain deep insights into what tokens are, their differences, and the implications of creating native tokens versus ERC20 tokens. - **Master Native Token Creation**: Learn how to create custom native tokens and understand when you need them versus when ERC20 tokens are sufficient. - **Leverage Precompiles**: Understand how to use the Native Minter and Fee Config Precompiles to create powerful tokenomics. - **Design Token Distribution**: Create effective vesting schedules, bonding curves, and airdrop strategies for your native tokens. - **Implement Governance**: Develop governance structures including DAOs and quadratic voting models for decentralized decision-making. But before diving into the technical implementation, we've included two essential think pieces to help you make informed decisions: **Essential Reading**: [Dapp vs L1](/academy/l1-native-tokenomics/dapp-vs-l1) - A critical analysis to determine whether your use case actually requires its own L1 with native tokenomics, or if deploying on an existing chain would be more appropriate. **Highly Recommended**: [Token Ownership](/academy/l1-native-tokenomics/token-ownership) - Not strictly necessary for implementation, this deep dive into the philosophical and practical aspects of token ownership will give you a much richer understanding of the underlying concepts at play in tokenomics design. # Token Ownership (/academy/avalanche-l1/l1-native-tokenomics/token-ownership) --- title: Token Ownership description: Everything you need to know about token ownership on blockchains updated: 2025-08-21 authors: [nicolasarnedo] icon: Key --- ## A Short History and Why It Matters Token ownership means recording who controls a digital asset on a public ledger, enforced by cryptography and consensus rather than by legal registries alone. It unlocks portable, programmable, and verifiable property rights for anything that can be represented in bits. ### From Scarcity to Programmability - Bitcoin (2009) solved digital scarcity and established provable, bearer‑style ownership “by code.” - Ethereum (2015) generalized ownership with smart contracts, enabling tokens: programmable representations of value, access, or rights. The 2014–2018 wave of token sales showed how ownership could be distributed globally without intermediaries—sometimes as utility access, sometimes (mistakenly) as implied equity. The lesson: tokens coordinate, but value capture must be designed, not assumed. ### What “Ownership” Means On‑Chain On blockchains, ownership is: - Verifiable: anyone can audit balances and provenance. - Portable: assets move across apps and wallets with private keys. - Programmable: rights can encode governance, access, royalties, staking, or fee distribution. - Composable: tokens integrate with DeFi, marketplaces, and tooling by common standards. ### Token Classes at a Glance Classification of crypto assets At a high level: - Fungible tokens (e.g., ERC‑20) track interchangeable units (balances, payments, governance power). - Non‑fungible tokens (e.g., ERC‑721) track unique items and their provenance (collectibles, credentials, rights). - Hybrids and multi‑tokens (e.g., ERC‑1155) combine both models for efficient gaming/commerce flows. ### Why Tokenized Ownership Works Tokens drastically reduce friction to distribute and align ownership. Like equity, they can motivate long‑term contribution; unlike equity, they are programmable (e.g., automatic reward streams, on‑chain voting) and readily tradable. But not every token confers the same rights: designs differ in value capture (fee burns, staking rewards, access discounts), transferability, and governance—and must consider regulation. ### Looking Ahead As you move into native tokens and ERC‑20s next, keep this lens: token ownership is a coordination primitive. Good designs clearly define what is owned (rights), how value accrues (mechanics), and how ownership changes hands (standards and controls). With those pillars, tokens become reliable building blocks for open, interoperable economies. # Course Completion Certificate (/academy/avalanche-l1/permissioned-l1s/certificate) --- title: Course Completion Certificate description: Get your completion certificate for the Permissioned L1s course updated: 2025-03-19 authors: [nicolasarnedo] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course! Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the Course (/academy/avalanche-l1/permissioned-l1s) --- title: Welcome to the Course description: Learn about L1 Validator Management for Permissioned Blockchains updated: 2025-07-15 authors: [nicolasrnedo] icon: Smile --- ## Permissioned L1s Welcome to the **Permissioned L1s** course! This course is designed to give you a deep understanding of configuring, launching and maintaining permissioned L1s on Avalanche. By the end of this course, you will have practical skills in deploying an L1 and managing the validator set in a Proof of Authority (PoA) network. ## Video ### What You'll Learn This comprehensive course will walk you through: - **Introduction** - P-Chain review, how Validator Manager Contracts use ICM & commonlys used Proxy Patterns - **Proof of Authority** - Understanding permissioning types for blockchains, what Proof of Authority is and the Validator Manager contract structure - **Create an L1** - Creating a Subnet, diving deep into the Transparent Proxy pattern, understanding genesis pre-deployed contracts and creating your L1 (recommended to first have created an L1 in the [Avalanche Fundamentals course](/academy/avalanche-l1/avalanche-fundamentals)) - **Validator Manager Deployment** - Deploying and configuring the Validator Manager Contract (VMC) on your new L1 - **Validator Manager Operations** - Adding, changing weights and removing validators - **Coming Soon... Multi-Sig Setup for PoA** - Implementing secure multi-signature governance with Safe/Ash wallets - **Coming Soon... Private L1s** - Configuring validator-only access and RPC node restrictions ### Let's Get Started! Each section builds upon previous knowledge, so we recommend completing the course in order. Happy learning and building! # Course Completion Certificate (/academy/avalanche-l1/permissionless-l1s/certificate) --- title: Course Completion Certificate description: Get your completion certificate for the Permissioned L1s course updated: 2025-03-19 authors: [nicolasarnedo] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course! Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the Course (/academy/avalanche-l1/permissionless-l1s) --- title: Welcome to the Course description: Learn about L1 Validator Management for Permissionless Blockchains updated: 2025-01-15 authors: [nicolasarnedo] icon: Smile --- ## Permissionless L1s Welcome to the **Permissionless L1s** course! This course is designed to give you a deep understanding of configuring, launching and maintaining permissionless L1s on Avalanche. By the end of this course, you will have practical skills in deploying an L1 with Proof of Stake (PoS) consensus and managing the validator set in a permissionless network. ### Prerequisites Before starting this course, we recommend completing: - [L1 Native Tokenomics](/academy/l1-native-tokenomics) - Understanding tokenomics fundamentals - [Permissioned L1s](/academy/permissioned-l1s) - Foundation in L1 validator management If you just completed these recently, you can go ahead and skip the [Review chapter](). If not, we recommend giving it a read, it will cover: - P-Chain fundamentals - Multi-Chain Architecture - Permissioned L1s - Native Tokenomics ### What You'll Learn This comprehensive course will walk you through: - **Proof of Stake** - Understanding PoS consensus, staking token selection, and liquid staking considerations - **Transformation Requirements** - Basic precmpoiles (Native Minter and Reward Manager) needed to transform your L1 into a permissionless network - **Basic Setup**(*optional*) - Deploying Validator Manager Contract, upgrading proxy, and initializing validator configuration - **Staking Manager Setup** - Deploying and configuring staking managers for native token staking - **Staking Manager Operations** - Adding, changing weights, removing validators, and handling delegation - **Node Licenses** - Understanding validator licensing and real-world examples ### Let's Get Started! Each section builds upon previous knowledge, so we recommend completing the course in order. Happy learning and building! # Course Completion Certificate (/academy/blockchain/blockchain-fundamentals/get-certificate) --- title: Course Completion Certificate updated: 2025-30-10 authors: [ashucoder9] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course. Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the Course (/academy/blockchain/blockchain-fundamentals) --- title: Welcome to the Course description: Learn about the basics of blockchain technology. updated: 2025-10-30 authors: [ashucoder9] icon: Smile --- Welcome to the Blockchain Fundamentals, an online course introducing you to blockchain! This course will provide you with a comprehensive understanding of the basic concepts of blockchains. By the end of these courses, you'll have the knowledge and skills to leverage the power of blockchain technology for your own projects and applications. We're excited to have you join us on this journey and can't wait to see what you'll create with Avalanche! ## Course Content We will cover the following topics in this course: - What is a Blockchain? - Deep Dive into Payments - Cryptography: Signature schemes - Consensus Mechanisms - Sybil Defense Mechanisms - Smart contracts - Tokenomics - Virtual Machines ## Prerequisites Anyone can take this course! ## Learning Outcomes By the end of this course, you will have a good understanding of the basic concepts of blockchain technology. You can evaluate your own understanding of the material through quizzes and claim a certificate for successful completion at the end. # Decentralization (/academy/blockchain/blockchain-fundamentals/xx-decentralization) --- title: Decentralization description: TBD updated: 2024-05-31 authors: [martineckardt] icon: BookOpen --- ## Moving from a centralized Entity to a Collective of Validators Blockchain systems achieve decentralization through the use of a network of validators, sometimes referred to as nodes, miners, or stakers, depending on the underlying consensus mechanism. Validators are responsible for verifying and securing transactions, maintaining the integrity of the blockchain, and ensuring that the system remains decentralized and trustless. Decentralization is achieved by distributing the responsibility of maintaining the network across numerous independent participants. Decentralization in blockchain is achieved through a network of validators. These are independent entities that run the blockchain software, perform computations, and ensure the integrity of the blockchain. Here’s how it works: Computation Execution: Each validator independently performs the same computation. For example, let's consider a simple computation: 5 + 3. Consensus Process: After performing the computation, validators share their results with each other. They then use a process called consensus to agree on the correct result. You can think of it as a election, where all validators vote on the correct answer. The consensus process ensures that all validators reach an agreement on the correct result, given that the majority is honest. This agreement is crucial for maintaining the integrity and security of the blockchain. If a validator tries to cheat or provide an incorrect result, the other validators will detect the discrepancy, and the cheating validator will be penalized. # Tokens (/academy/blockchain/blockchain-fundamentals/xx-tokens) --- title: Tokens description: TBD updated: 2024-05-31 authors: [martineckardt] icon: Notebook --- Tokens are a concept that existed in societies for a long time. Tokens can be used to represent value. The most common valuable tokens we use every day are fiat currencies, like the US dollar. But there are also other kinds of tokens, such as points or miles in loyalty programs. Today we can also tokenize other assets, such as property titles. ## Fungible Tokens Fungibility means that different tokens can be considered of equal value. Let's take for example two one dollar bills. Most people will not care if they get one or the other. They both offer the same utility. ## Non-Fungible Tokens Non-fungible tokens are not considered equal. The most prominent use cases are Art-NFTs. While the tokens may follow a standard of how they can be transfered, they may not be interchangeable. Two pieces of art may have very different values. # Welcome to the course (/academy/blockchain/encrypted-erc) --- title: Welcome to the course description: Learn about the encrypted-ERC (eERC) token standard from AvaCloud. updated: 2025-07-21 authors: [alejandro99so] icon: Lock --- Welcome to the **eERC Token Standard** course! This course is designed to give you a deep understanding of encrypted ERC (eERC), a privacy preserving ERC-20 like token standard developed by AvaCloud, enabling confidential transactions on EVM-compatible blockchains. By the end of this course, you will understand the privacy limitations of traditional token standards, how eERC solves them, and how to create and integrate your own private tokens. ## Course Content ### 1. What is a Digital Asset? - **Standards**: ERC-20, ERC-721, ERC-1155 - **Current Uses**: How companies, DeFi protocols, and projects are using them today. - **Limitations**: Challenges companies face when trying to adopt these standards (scalability, privacy, compliance). ### 2. Real Privacy - **How Private is the Blockchain?**: Transparency vs confidentiality. - **Compliance**: Why regulatory alignment matters for privacy tokens. - **Necessities Solved with Privacy**: Situations where privacy is essential (e.g., sensitive transactions, enterprise use). ### 3. Encrypted Tokens - **What Kind of Privacy Does eERC Provide?**: Hidden balances, confidential transfers. - **Comparison: ERC-20 vs eERC**: Feature-by-feature breakdown. - **Technology Behind eERC**: Zero-knowledge proofs and homomorphic encryption. ### 4. Usability of eERC - **Standalone vs Converter Contract**: When to use each mode. - **Use Cases**: From DeFi to regulated financial institutions. ### 5. eERC Contracts Flow - **Step-by-Step**: Creating your own eERC token and considerations. - **Kinds of ZKProof to Use**: Selection criteria based on use case. - **User Flow**: How to interact with eERC as an end user. --- ## Prerequisites Before starting this course, you should have: ### Blockchain Fundamentals Familiarity with EVM-based blockchains and ERC token standards. ### Development Tools Basic knowledge of Solidity and comfort using TypeScript will help, especially when working with the Privacy functions. --- ## Learning Outcomes By the end of this course, you will: - Understand the **purpose and innovation** behind the eERC standard. - Compare **traditional ERC standards** with encrypted ERC for privacy and compliance. - Identify the **key benefits, architecture, and technical underpinnings** of eERC. - Differentiate between **Standalone** and **Converter** implementation modes. - Recognize **real-world use cases** where privacy and auditability are essential. - Create and deploy your own **eERC token** following a step-by-step process. - Understand how to **interact with eERC as a user** in different scenarios. # Understanding ERC-721 Tokens (/academy/blockchain/nft-deployment/01-erc721-standard) --- title: Understanding ERC-721 Tokens description: Learn about the Non-Fungible Token Standard updated: 2024-11-20 authors: [nicolasarnedo] icon: Book --- Previously, we explored the ERC-20 interface, a contract standard which allowed for the implementation of fungible tokens. However, there is one important use case that the ERC-20 standard cannot support: **non-fungible assets**. ## What is Fungibility? To understand why we need ERC-721, let's first understand fungibility: - **Fungible tokens** (ERC-20): Each token is identical and interchangeable. 1 USDC is always equal to 1 USDC. - **Non-fungible tokens** (ERC-721): Each token is unique and has its own distinct value. One piece of digital art is not equal to another. ## Why ERC-721? Imagine you wanted to represent an art collection within a smart contract. The smart contract would need: - The ability to query the "balance" (i.e. the art holdings) of a particular user - The ability to transfer art pieces from one account to another - The ability to get information about the art collection The biggest difference between an ERC-20 token contract and an art collection is the **fungibility** of individual items. ERC-20 tokens are inherently fungible, while items in an art collection are not (since each piece varies in value). To account for use cases like art collections, digital collectibles, gaming items, and more, the **ERC-721 standard** was introduced. ## The ERC-721 Interface Formally, ERC-721 is a standard for non-fungible tokens. Below is the interface of the ERC-721 token from its original proposal: ```solidity interface ERC721 { /// @dev This emits when ownership of any NFT changes by any mechanism. event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); /// @dev This emits when the approved address for an NFT is changed or reaffirmed. event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); /// @dev This emits when an operator is enabled or disabled for an owner. event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); /// @notice Count all NFTs assigned to an owner function balanceOf(address _owner) external view returns (uint256); /// @notice Find the owner of an NFT function ownerOf(uint256 _tokenId) external view returns (address); /// @notice Transfers the ownership of an NFT from one address to another address function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable; /// @notice Transfers the ownership of an NFT from one address to another address function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable; /// @notice Transfer ownership of an NFT function transferFrom(address _from, address _to, uint256 _tokenId) external payable; /// @notice Change or reaffirm the approved address for an NFT function approve(address _approved, uint256 _tokenId) external payable; /// @notice Enable or disable approval for a third party ("operator") to manage all assets function setApprovalForAll(address _operator, bool _approved) external; /// @notice Get the approved address for a single NFT function getApproved(uint256 _tokenId) external view returns (address); /// @notice Query if an address is an authorized operator for another address function isApprovedForAll(address _owner, address _operator) external view returns (bool); } ``` ## Key Differences from ERC-20 ### Token Identification **ERC-20:** ```solidity mapping(address => uint) balances; ``` **ERC-721:** ```solidity mapping(address => uint) balances; // Number of NFTs owned by each address mapping(uint => address) holders; // Owner of each specific token ID ``` The key difference is the addition of the `holders` mapping, which tracks which address owns each individual token by its unique ID. This allows each token to be distinct and non-fungible. ### Transfer Functions ERC-721 introduces `safeTransferFrom`, which includes a safety check to ensure the receiving address can handle NFTs. This prevents tokens from being permanently lost if sent to a contract that doesn't support them. ### Approval Mechanism Like ERC-20, ERC-721 supports approvals, but with two levels: - **Single token approval**: Approve someone to transfer a specific NFT - **Operator approval**: Approve someone to manage all your NFTs ## Common Use Cases ERC-721 tokens are used for: - **Digital Art**: Unique artwork pieces with verifiable ownership - **Collectibles**: Trading cards, virtual items, and memorabilia - **Gaming Assets**: In-game items, characters, and land - **Real Estate**: Tokenized property ownership - **Identity**: Unique credentials and certificates - **Domain Names**: Blockchain-based domain systems ## Next Steps Now that you understand the ERC-721 standard, you're ready to create your own NFT collection. In the next section, we'll prepare the files needed for your NFTs, including images and metadata. # Prepare NFT Files (/academy/blockchain/nft-deployment/02-prepare-nft-files) --- title: Prepare NFT Files description: Learn how to prepare and upload your NFT images and metadata to IPFS updated: 2024-11-20 authors: [Andrea Vargas, Ash, martineckardt] icon: BookOpen --- The first step of setting up an NFT smart contract is having your NFT files ready to use. In this guide, we'll upload files to [Pinata](https://www.pinata.cloud/), a pinning service that prevents files from being garbage collected on IPFS. If you don't already have a Pinata account, please [create one](https://www.pinata.cloud/) before proceeding. The free tier is sufficient for this tutorial. ## Preparing the Images This tutorial will create only 1 NFT, however, if you're interested in creating more, you're more than welcome to do so. ![Original NFT Photo](/images/nft-files1.jpeg) ### Organizing Your Images 1. Create a folder on your computer for your NFT images 2. Name your image file `0` (without any file extension visible) 3. If creating multiple NFTs, continue naming them sequentially: `0`, `1`, `2`, etc. **Important**: Some projects start file names with `0`, and others with `1`. That choice must be consistent with your smart contract code. For this tutorial, we'll start with `0`. ![Image Folder](/images/nft-files2.png) ### Uploading Images to Pinata 1. Log into your Pinata dashboard 2. Click the **Upload** button on the left sidebar 3. Select **Folder** from the upload options ![Pinata Dashboard](/images/nft-files3.png) ![Folder Button](/images/nft-files4.png) 4. Select the folder containing your image(s) 5. Confirm the upload if prompted by your browser ![Confirm Upload](/images/nft-files5.png) 6. Give your folder a descriptive name (this helps organize multiple collections) 7. Click **Upload** and wait for the process to complete Once the upload is complete, you'll see your folder in the dashboard: ![Uploaded Image Folder](/images/nft-files6.png) ### Getting Your Image URL 1. Click on the folder name to view it through the Pinata gateway 2. Right-click on your image and copy the URL 3. Save this URL - you'll need it for the metadata file Example URL: `https://gateway.pinata.cloud/ipfs/QmPWbixyMsaNkR9v612bBFbncKGmgXDKz9CgMtDDD7Bymw/0.png` ## Creating Metadata Files Now that we have the image uploaded, we need to create matching metadata files. Since we're creating an ERC-721 NFT, we'll use the standard metadata format used by marketplaces like [Joepegs.com](https://joepegs.com/). ### Metadata Structure The basic [metadata structure](https://docs.opensea.io/docs/metadata-standards#metadata-structure) looks like this: ```json { "name": "", "tokenId": 0, "image": "", "description": "", "attributes": [] } ``` ### Populating the Metadata Let's fill in each field: - **name**: Choose any name for your NFT - **tokenId**: Use `0` to correspond with your first image (increment for additional NFTs) - **image**: Paste the image URL you copied from Pinata - **description**: Describe your NFT - **attributes**: Optional array for traits (useful for collections with layers/rarity) Example completed metadata: ```json { "name": "Cool Photography", "tokenId": 0, "image": "https://gateway.pinata.cloud/ipfs/QmPWbixyMsaNkR9v612bBFbncKGmgXDKz9CgMtDDD7Bymw/0.png", "description": "A cool image" } ``` The `attributes` field is particularly useful for NFT collections with multiple layers. It's used to calculate rarity and rank NFTs based on how frequently their traits appear throughout the collection. For this simple example, we've omitted it. ### Saving the Metadata File 1. Save this JSON content as a file named `0` (matching your image name) 2. **Remove the file extension** so it's just `0`, not `0.json` - [Mac instructions](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac) - [Windows instructions](https://www.techwalla.com/articles/how-to-remove-file-extensions) **Critical**: The metadata file must NOT have a `.json` extension when uploaded to Pinata. The IPFS gateway will serve it as a directory, and your smart contract will be able to fetch it correctly. 3. Place the metadata file in a **separate folder** from your images ![Metadata Folder](/images/nft-files7.png) ### Uploading Metadata to Pinata 1. Repeat the folder upload process for your metadata folder 2. Follow the same steps as you did for the images 3. Once uploaded, both folders should appear in your dashboard ![Uploaded Folders](/images/nft-files8.png) ### Getting Your Base URI 1. Click on the **metadata folder** to view it in the IPFS gateway 2. Copy the URL - this is your **Base URI** 3. **Important**: Use only the folder URL, not the individual file URL Example Base URI: `https://gateway.pinata.cloud/ipfs/QmYdWxbiwsfsYcW1CYQPgYujAc9FMLPG3fgFcxFskbSsFa` The smart contract will automatically append the token ID to this base URI when fetching metadata for each NFT. ## File Preparation Checklist Before moving to the next section, ensure you have: - ✅ Images uploaded to Pinata in a folder - ✅ Metadata files created with correct structure - ✅ Metadata files named to match image files (0, 1, 2, etc.) - ✅ Metadata files uploaded to Pinata in a **separate** folder - ✅ Base URI copied and saved ## Next Steps Now that your NFT files are prepared and uploaded to IPFS, you're ready to create and deploy your smart contract. In the next section, we'll use OpenZeppelin's Contract Wizard to generate an ERC-721 contract customized for your collection. # Create Your NFT Smart Contract (/academy/blockchain/nft-deployment/03-create-smart-contract) --- title: Create Your NFT Smart Contract description: Use OpenZeppelin to generate a customized ERC-721 contract updated: 2024-11-20 authors: [Andrea Vargas, Ash, martineckardt] icon: BookOpen --- Now that your NFT files are prepared, it's time to create the smart contract that will manage your NFT collection. We'll use [OpenZeppelin](https://docs.openzeppelin.com/), a trusted library for building secure smart contracts. ## Why OpenZeppelin? OpenZeppelin provides battle-tested, audited smart contract implementations that follow industry standards. Their [Contract Wizard](https://wizard.openzeppelin.com/#erc721) allows you to generate customized contracts without writing code from scratch, reducing the risk of security vulnerabilities. ## Using the Contract Wizard ### Step 1: Access the Wizard Navigate to [wizard.openzeppelin.com/#erc721](https://wizard.openzeppelin.com/#erc721) ![Contract Wizard](/images/nft-collection4.png) ### Step 2: Select ERC-721 Make sure the **ERC-721** tab is selected. This will create a contract in the [Solidity programming language](https://docs.soliditylang.org/) specifically for non-fungible tokens. ### Step 3: Configure Basic Settings The wizard provides a template contract that you'll customize: **Name**: Give your NFT collection a name (e.g., "Photography") **Symbol**: Choose a short symbol for your collection (e.g., "FOTO") **Base URI**: Paste the metadata folder URL you saved from Pinata Example: `https://gateway.pinata.cloud/ipfs/QmYdWxbiwsfsYcW1CYQPgYujAc9FMLPG3fgFcxFskbSsFa` ![Contract Wizard Populated](/images/nft-collection5.png) ### Step 4: Add Minting Functionality Check the following boxes to add essential features: **Mintable**: Adds a `safeMint` function to create new NFTs **Auto Increment Ids**: Automatically assigns sequential token IDs This will automatically check **Ownable**, which restricts the `safeMint` function to the contract owner. ![Contract Wizard SafeMint](/images/nft-collection6.png) ## Understanding the Generated Contract Let's examine the key parts of your generated contract: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract Photography is ERC721, Ownable { uint256 private _nextTokenId; string private _baseTokenURI; constructor(address initialOwner) ERC721("Photography", "FOTO") Ownable(initialOwner) { _baseTokenURI = "https://gateway.pinata.cloud/ipfs/QmYdWxbiwsfsYcW1CYQPgYujAc9FMLPG3fgFcxFskbSsFa/"; } function _baseURI() internal view override returns (string memory) { return _baseTokenURI; } function safeMint(address to) public onlyOwner { uint256 tokenId = _nextTokenId++; _safeMint(to, tokenId); } } ``` ### Key Components **Inheritance**: Your contract inherits from `ERC721` (the standard) and `Ownable` (access control) **Constructor**: Sets the collection name, symbol, and base URI when deployed **_baseURI()**: Returns the base URI that will be combined with token IDs to fetch metadata **safeMint()**: Creates new NFTs and assigns them sequential IDs The `onlyOwner` modifier means only the contract owner can mint NFTs. For a public mint, you would remove this modifier and add payment logic. ## About the safeMint Function The current `safeMint` function: - Mints one NFT at a time - Can mint to any address (not just the owner's) - Charges no fee (except gas) - Functions as an "airdrop" mechanism This is perfect for: - Personal collections - Gifting NFTs - Controlled distributions - Testing your contract For a public sale, you would modify this function to: - Accept payment (e.g., `payable` with price checks) - Remove the `onlyOwner` modifier - Add supply limits - Implement per-wallet minting limits ## Next Steps Your smart contract is now ready! Click **Open in Remix** at the top of the wizard to prepare for deployment. ![Contract Wizard Open Remix](/images/nft-collection7.png) In the next section, we'll deploy this contract to the Avalanche network and mint your first NFT. # Deploy and Mint Your NFT (/academy/blockchain/nft-deployment/04-deploy-and-mint) --- title: Deploy and Mint Your NFT description: Deploy your ERC-721 contract to Avalanche and mint your first NFT updated: 2024-11-20 authors: [Andrea Vargas, Ash, martineckardt] icon: BookOpen --- Now it's time to deploy your NFT smart contract to the Avalanche network and mint your first NFT! ## Prerequisites Before deploying, ensure you have: ### Core Wallet Extension You'll need the Core browser extension to fund and deploy your contract. If you haven't installed it yet: 1. Download [Core Extension](https://chrome.google.com/webstore/detail/core-crypto-wallet-nft-ex/agoakfejjabomempkjlepdflaleeobhb) 2. Create or import a wallet 3. Enable **Testnet Mode**: - Go to **Settings** → **Advanced** - Turn on **Testnet Mode** - Core will automatically switch to Fuji Testnet ![Settings image 1](/images/nft-collection1.png) ![Settings image 2](/images/nft-collection2.png) **Other Wallets**: If using MetaMask or another wallet, add Fuji Testnet manually: - **Network Name**: Avalanche Fuji C-Chain - **RPC URL**: `https://api.avax-test.network/ext/bc/C/rpc` - **Chain ID**: `43113` - **Symbol**: AVAX - **Explorer**: `https://testnet.snowtrace.io` ### Testnet AVAX You'll need AVAX on the Fuji testnet to pay for gas fees. **Option 1 (Recommended)**: - Create a [Builder Hub account](https://build.avax.network/login) - Connect your wallet to receive testnet AVAX automatically **Option 2**: - Use the [Avalanche Faucet](https://core.app/tools/testnet-faucet/) - If you have AVAX on Mainnet, paste your C-Chain address and request test tokens - Otherwise, request a faucet coupon on [Guild](https://guild.xyz/avalanche) ![Avalanche Faucet](/images/nft-collection3.png) ## Deploying with Remix IDE [Remix IDE](https://remix.ethereum.org/) is a browser-based Solidity compiler that makes it easy to compile and deploy smart contracts without installing any software. ### Step 1: Compile Your Contract After importing your contract from OpenZeppelin Wizard: 1. Click the **Compile** button on the left sidebar (or press `Ctrl/Cmd + S`) 2. Wait for the green checkmark to appear ![Remix Compile](/images/nft-collection8.png) You may see options to "Publish on IPFS" or "Publish on Swarm" - these aren't necessary for this tutorial. ### Step 2: Configure Deployment 1. Click the **Deploy & Run Transactions** tab (bottom icon on left sidebar) ![Remix Deploy Page](/images/nft-collection9.png) 2. Change the **Environment** dropdown to **Injected Provider - MetaMask** ![Remix Web3](/images/nft-collection10.png) 3. Core will prompt you to connect - approve the connection 4. Verify the connection by checking that the **Account** matches your Core address ![Remix account](/images/nft-collection11.png) ![Core account](/images/nft-collection12.png) ### Step 3: Select Your Contract In the **Contract** dropdown, select your contract (it will have the name you gave it in the wizard). ![Remix Contract](/images/nft-collection13.png) ### Step 4: Deploy 1. Click the orange **Deploy** button 2. Core will open and ask you to confirm the transaction 3. Review the gas fee and click **Confirm** ![Core Accept](/images/nft-collection14.png) 4. Wait for the transaction to be confirmed Your deployed contract will appear under **Deployed Contracts**: ![Remix Record](/images/nft-collection15.png) ### Step 5: Verify on Snowtrace 1. Copy your contract address from Remix 2. Open [Snowtrace Testnet Explorer](https://testnet.snowtrace.io/) 3. Paste the address in the search bar You'll see your contract information, including the deployment transaction: ![Snowtrace](/images/nft-collection16.png) ## Minting Your First NFT Now that your contract is deployed, let's mint an NFT! ### Step 1: Access the safeMint Function 1. In Remix, expand your deployed contract 2. You'll see a list of available functions ![Remix Functions](/images/nft-collection17.png) 3. Click the dropdown arrow next to **safeMint** to expand it ![safeMint function](/images/nft-collection18.png) ### Step 2: Mint to Your Address 1. Copy your Core wallet address 2. Paste it into the `to` address field 3. Click the orange **transact** button You can mint to any address, not just your own. This is useful for gifting NFTs or airdrops! 4. Confirm the transaction in Core 5. Wait for the green checkmark in the Remix terminal ![Remix Confirm Mint](/images/nft-collection19.png) ### Step 3: Verify the Mint 1. Return to your contract page on Snowtrace 2. Refresh the page 3. You should see a new transaction for `safeMint` ![Snowtrace Mint](/images/nft-collection20.png) 4. Click on the [TX Hash](https://testnet.snowtrace.io/tx/0x8b698ac72bd20b2a640167c5d9bacdcbb3d86fd696eb8cde6f28347f6f99a2c9) to view details ![Snowtrace Transaction](/images/nft-collection21.png) Congratulations! You've successfully minted your first NFT! 🎉 ## Viewing Your NFT Your NFT is now: - Recorded on the Avalanche blockchain - Owned by your wallet address - Linked to your metadata and image on IPFS You can view it: - On Snowtrace (under the "Token Transfers" tab) - In your Core wallet (NFT section) - On NFT marketplaces that support Avalanche testnet ## Deploying to Mainnet When you're ready to launch your project on Mainnet, the process is identical except: 1. **Switch to Mainnet**: Disable Testnet Mode in Core 2. **Get Real AVAX**: You'll need actual AVAX to pay for gas fees 3. **Use Mainnet Snowtrace**: View transactions at [snowtrace.io](https://snowtrace.io/) 4. **List on Marketplaces**: Your NFTs can be listed on platforms like [Joepegs](https://joepegs.com/) **Before Mainnet Deployment**: - Thoroughly test your contract on testnet - Consider a professional audit for valuable collections - Ensure your IPFS files are permanently pinned - Have a clear minting strategy and pricing ## Next Steps Now that you've deployed and minted your NFT, you might want to: - Mint additional NFTs from your collection - Learn about token URIs and metadata standards - Explore advanced features like royalties and allowlists - Build a custom minting dApp for your collection In the next section, we'll dive deeper into how token URIs work and how your NFTs connect to their metadata. # Understanding Token URIs (/academy/blockchain/nft-deployment/05-token-uris) --- title: Understanding Token URIs description: Learn how NFTs connect to their metadata and images updated: 2024-11-20 authors: [Andrea Vargas, Ash, martineckardt] icon: BookOpen --- Now that you've deployed your NFT collection, let's understand how your on-chain tokens connect to their off-chain metadata and images through **Token URIs**. ## What is a Token URI? A Token URI (Uniform Resource Identifier) is a unique string that points to a JSON file containing an NFT's metadata. This metadata includes: - The NFT's name - Description - Image URL - Attributes/traits - Other properties ## How Token URIs Work When you call `tokenURI(tokenId)` on an ERC-721 contract, it returns a complete URL by combining: ``` Base URI + Token ID = Token URI ``` ### Example If your base URI is: ``` https://gateway.pinata.cloud/ipfs/QmYdWxbiwsfsYcW1CYQPgYujAc9FMLPG3fgFcxFskbSsFa/ ``` And you query token ID `0`, the contract returns: ``` https://gateway.pinata.cloud/ipfs/QmYdWxbiwsfsYcW1CYQPgYujAc9FMLPG3fgFcxFskbSsFa/0 ``` This URL points to your metadata file on IPFS. ## The Metadata Standard The JSON file at the Token URI follows the [OpenSea Metadata Standard](https://docs.opensea.io/docs/metadata-standards): ```json { "name": "Cool Photography #0", "description": "A cool image from my photography collection", "image": "https://gateway.pinata.cloud/ipfs/QmPWbixyMsaNkR9v612bBFbncKGmgXDKz9CgMtDDD7Bymw/0.png", "attributes": [ { "trait_type": "Background", "value": "Blue" }, { "trait_type": "Style", "value": "Abstract" } ] } ``` ### Metadata Fields **name**: The NFT's display name (often includes the token ID) **description**: A detailed description of the NFT **image**: The URL to the actual image file (also on IPFS) **attributes**: An array of traits that define the NFT's characteristics ## Why Use IPFS? IPFS (InterPlanetary File System) is preferred for NFT storage because: **Decentralized**: No single point of failure **Content-Addressed**: Files are identified by their content, not location **Permanent**: Files remain accessible as long as they're pinned **Immutable**: Content can't be changed without changing the hash ### IPFS URLs IPFS URLs follow this format: ``` https://gateway.pinata.cloud/ipfs/[CONTENT_HASH]/[FILE_NAME] ``` The content hash (e.g., `QmYdWxbiwsfsYcW1CYQPgYujAc9FMLPG3fgFcxFskbSsFa`) uniquely identifies your folder on IPFS. ## URI Storage in Your Contract In your OpenZeppelin contract, the base URI is stored and accessed like this: ```solidity contract Photography is ERC721, Ownable { string private _baseTokenURI; constructor(address initialOwner) ERC721("Photography", "FOTO") Ownable(initialOwner) { _baseTokenURI = "https://gateway.pinata.cloud/ipfs/QmYdWxbiwsfsYcW1CYQPgYujAc9FMLPG3fgFcxFskbSsFa/"; } function _baseURI() internal view override returns (string memory) { return _baseTokenURI; } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "Token does not exist"); return string(abi.encodePacked(_baseURI(), Strings.toString(tokenId))); } } ``` ### How It Works 1. `tokenURI()` is called with a token ID 2. The function checks if the token exists 3. It concatenates the base URI with the token ID 4. Returns the complete URL to the metadata ## Advanced URI Features ### URI Storage Extension OpenZeppelin provides a `ERC721URIStorage` extension that allows setting individual URIs for each token: ```solidity import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; contract MyNFT is ERC721URIStorage { function mint(address to, uint256 tokenId, string memory uri) public { _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } } ``` This is useful when: - Each NFT has a completely different metadata structure - You want to update metadata after minting - Your collection doesn't follow a sequential pattern ### Updating Base URI You might want to update the base URI after deployment to: - Move to a different IPFS gateway - Migrate to a custom domain - Update to revealed metadata (for mystery drops) Add this function to your contract: ```solidity function setBaseURI(string memory newBaseURI) public onlyOwner { _baseTokenURI = newBaseURI; } ``` Be cautious when updating URIs. Changing metadata after minting can be controversial and may affect trust in your collection. ## Best Practices ### 1. Consistent File Naming Ensure your metadata files are named to match token IDs: - Token 0 → file named `0` - Token 1 → file named `1` - And so on... ### 2. Include Trailing Slash Always include a trailing slash in your base URI: ``` ✅ https://gateway.pinata.cloud/ipfs/QmHash/ ❌ https://gateway.pinata.cloud/ipfs/QmHash ``` ### 3. Test Before Minting Before minting, manually test your URIs: ``` Base URI + 0 should return valid JSON Base URI + 1 should return valid JSON ``` ### 4. Pin Your Files Ensure your files are permanently pinned on IPFS. Services like Pinata provide pinning to prevent garbage collection. ### 5. Use IPFS Gateways Wisely While Pinata's gateway works well, consider: - Using multiple gateways for redundancy - Setting up your own IPFS node for large collections - Using a CDN for faster loading ## Viewing Token URIs You can view your NFT's token URI in several ways: ### 1. Remix IDE In your deployed contract, call `tokenURI` with a token ID: ```solidity tokenURI(0) // Returns the full URI for token 0 ``` ### 2. Snowtrace On your contract page, go to "Read Contract" and call `tokenURI`. ### 3. Programmatically Using ethers.js: ```javascript const tokenURI = await contract.tokenURI(0); console.log(tokenURI); ``` ## Common Issues ### "Token does not exist" This error means you're querying a token ID that hasn't been minted yet. ### 404 Not Found If the URI returns 404: - Check that your metadata files are uploaded to IPFS - Verify the file names match token IDs - Ensure the base URI has a trailing slash ### Invalid JSON If marketplaces can't read your metadata: - Validate your JSON syntax - Ensure all required fields are present - Check that the file has no extension ## Next Steps Now that you understand how token URIs work, you're ready to: - Create larger NFT collections with multiple tokens - Implement reveal mechanisms for mystery drops - Build custom metadata structures for your use case - Integrate your NFTs with marketplaces and dApps Congratulations on completing the NFT Deployment course! You now have the knowledge to create, deploy, and manage your own NFT collections on Avalanche. # Welcome to NFT Deployment (/academy/blockchain/nft-deployment) --- title: Welcome to NFT Deployment description: Learn how to create, prepare, and deploy your own NFT collection on Avalanche updated: 2024-11-20 authors: [Andrea Vargas, Ash, martineckardt] icon: Smile --- In this course, you will learn how to deploy your own NFT (Non-Fungible Token) collection on the Avalanche network. ## Why Take This Course? NFTs have revolutionized digital ownership, enabling creators to tokenize unique digital assets like art, collectibles, and more. This course will guide you through the entire process of creating and deploying an NFT collection, from understanding the ERC-721 standard to preparing your files and deploying your smart contract. ## Course Content ### ERC-721 Token Standard Learn about the ERC-721 standard, the foundation for non-fungible tokens, and understand how it differs from fungible tokens like ERC-20. ### Preparing NFT Files Discover how to prepare your NFT images and metadata files, and upload them to IPFS using Pinata for decentralized storage. ### Creating the Smart Contract Use OpenZeppelin's Contract Wizard to generate a customized ERC-721 smart contract for your NFT collection. ### Deploying on Avalanche Deploy your NFT collection to the Avalanche C-Chain using Remix IDE and mint your first NFT. ### Understanding Token URIs Learn how token URIs work and how they connect your on-chain NFTs to their off-chain metadata and images. ## Prerequisites ### Blockchain / Web3 This course is meant for people with some experience in web3. You should be familiar with: - **Wallets**: What they are and how to create one - **dApps**: What a decentralized application is and how to interact with one - **Transactions**: How to send and confirm blockchain transactions If any of this is unclear, we strongly recommend taking the Blockchain Fundamentals course first. ### Software Development You will need a general understanding of software development: - **Programming**: Familiarity with basic programming concepts like variables, functions, and data structures - **Solidity Basics**: Understanding of smart contracts (covered in the Intro to Solidity course) - **IDE**: Familiarity with web-based development tools like Remix ## Learning Outcomes By the end of this course, you will be able to: - Understand the ERC-721 token standard and how NFTs work - Prepare and upload NFT files to IPFS using Pinata - Create a customized ERC-721 smart contract using OpenZeppelin - Deploy your NFT collection to Avalanche C-Chain - Mint NFTs from your deployed contract - Understand token URIs and metadata standards - View your NFTs on blockchain explorers ## Tools You'll Use - **Pinata**: For storing NFT images and metadata on IPFS - **OpenZeppelin Wizard**: To generate your ERC-721 smart contract - **Remix IDE**: To compile and deploy your contract - **Core Wallet**: To manage transactions and pay gas fees - **Snowtrace**: To view your deployed contract and transactions # Welcome to the course (/academy/blockchain/solidity-foundry) --- title: Welcome to the course description: Learn the basics about programming Smart Contracts in Solidity for an EVM Blockchain updated: 2024-05-31 authors: [Andrea Vargas] icon: Smile --- In this course, you will learn how to build Solidity dApps on Avalanche. ## Why Take This Course? A significant innovation in blockchain is the development of multi-chain systems, like Avalanche, which provide a significant improvement in scalability, interoperability, and flexibility. While a blockchain on Avalanche can be run with any VM, the most prominent choice currently is the Ethereum Virtual Machine (EVM). Users can deploy their own logic in the form of smart contracts to the EVM. These smart contracts can be written in Solidity. Learning Solidity can enable you to leverage the features of blockchain for your dApp. ## Course Content ### Smart Contracts In the first section, we will look at what smart contracts are, their basic structure and how they work. ### Hello World Part I In this section we will look at the primitive types (strings, integers, bool, ...) and function as well as the Solidity file structure. ### Hello World Part II We will look at control flows (if & else), data structures and constructors. Furthermore, we learn about inheritance from other contracts and modifiers and events. ### Contract Standardization You will learn how contracts can be standardized and how inheritance and interfaces can help us to do so. ## Prerequisites ### Blockchain / Web3 This course is meant for people with a some experience when it comes to web3. You should be familiar with these concepts: - Wallet: What they are and how to create one - dApp: What a decentralized application is and how to interact with one If any of this is unclear, we strongly recommend taking the Avalanche Fundamentals and Subnet Architecture courses first, that give a soft introduction into these topics from a user stand point. ### Software Development You will need a general understanding of Software Development. Therefore, we recommend: - Programming: Familiarity with most basic concepts of programming, such as variables, control flows (if, else) and loops. All exercises will consist of writing small contracts in Solidity. - IDE: It will help if you're generally familiar with the concept of an integrated developer environment. We will be leveraging Remix. ## Learning Outcomes By the end of this course, students will: - Interact and Deploy contracts using Foundry - Get familiar with ERC20 and ERC721 token standards - Understand important concepts such as inheritance, modifiers, and events. - Apply their knowledge by building their own smart contracts. # Course Completion Certificate (/academy/blockchain/x402-payment-infrastructure/get-certificate) --- title: Course Completion Certificate updated: 2025-11-03 authors: [Federico Nardelli] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; Congratulations! You've completed the x402 Payment Infrastructure course. Let's verify your progress and get your certificate. Thank you for taking this course! You now have the knowledge to build instant, permissionless payment infrastructure on Avalanche using the x402 protocol. We hope you'll use what you've learned to create innovative payment-enabled applications and contribute to the future of machine-to-machine commerce. # Welcome to the Course (/academy/blockchain/x402-payment-infrastructure) --- title: Welcome to the Course description: Learn about the x402 protocol for instant, permissionless HTTP-native payments on Avalanche. updated: 2025-11-03 authors: [Federico Nardelli] icon: Coins --- ## Why Take This Course? The x402 protocol is revolutionizing how payments work on the internet by activating HTTP 402 "Payment Required" status code for instant, permissionless transactions. Built on blockchain technology, x402 enables seamless machine-to-machine payments, making it perfect for AI agents, micropayments, and API monetization. Traditional payment systems are slow, expensive, and require accounts, KYC, and complex integrations. x402 eliminates these friction points with 2-second settlement times, zero protocol fees, and no account requirements. Payments are gasless for end users—facilitators sponsor the minimal blockchain gas fees (~\$0.001 on Avalanche with current network conditions). For developers building on Avalanche, x402 leverages the network's low fees and fast finality to create an ideal environment for high-frequency micropayments. ## Course Content - [Introduction to x402](/academy/blockchain/x402-payment-infrastructure/02-introduction/01-what-is-x402) - [Technical Architecture](/academy/blockchain/x402-payment-infrastructure/03-technical-architecture/01-payment-flow) - [x402 on Avalanche](/academy/blockchain/x402-payment-infrastructure/04-x402-on-avalanche/01-why-avalanche) ## Prerequisites Before taking this course, you should have: - **Blockchain Fundamentals:** Basic understanding of blockchain technology and how transactions work. - **Avalanche Fundamentals:** Familiarity with the Avalanche network, particularly the C-Chain. - **HTTP/APIs:** Understanding of HTTP protocols, status codes, and API development. - **Programming:** Basic knowledge of JavaScript/TypeScript or another programming language. - **Smart Contracts (Optional):** Helpful but not required for understanding payment verification. ## Learning Outcomes By the end of this course, you will: - Understand the x402 protocol and how it activates HTTP 402 for internet-native payments. - Learn the complete payment flow from request to settlement, including facilitator roles. - Discover why Avalanche is ideal for x402 implementations with its low fees and fast finality. - Explore different x402 facilitator implementations available on Avalanche (Thirdweb, PayAI, Ultravioleta DAO, x402-rs). - Implement x402 middleware in your own applications and configure endpoint pricing. - Build use cases for AI agent payments, micropayments, and API monetization. - Master security best practices and scaling considerations for production deployments. # Course Completion Certificate (/academy/entrepreneur/foundations-web3-venture/certificate) --- title: Course Completion Certificate updated: 2025-09-05 authors: [nicolasarnedo] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course! Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Entrepreneur Academy (/academy/entrepreneur/foundations-web3-venture) --- title: Entrepreneur Academy description: Building a Successful Web3 Startup updated: 2025-07-31 authors: [nicolasarnedo] icon: Smile --- ## Level 1: Foundations of a Web3 Venture (Module 1 - 3) These three modules are designed to give you a strong foundation for building and scaling your startup with confidence. You’ll begin by setting up your company on solid ground, mastering the essential legal, financial, compliance, and security principles that safeguard your business. From there, you’ll learn to shape and refine your vision through the Business Model Canvas, ensuring your strategy is both sustainable and customer-focused. Finally, you’ll dive deep into understanding the people who matter most—your stakeholders and users—by identifying their needs, creating personas, and translating insights into impactful solutions. Together, these modules equip you with the practical tools to launch, grow, and sustain a successful venture. # Course Completion Certificate (/academy/entrepreneur/fundraising-finance/certificate) --- title: Course Completion Certificate updated: 2025-09-05 authors: [nicolasarnedo] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course! Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to the Course (/academy/entrepreneur/fundraising-finance) --- title: Welcome to the Course description: Building a Successful Web3 Startup — Navigating Avalanche Foundation Grants updated: 2025-07-31 authors: [nicolasarnedo] icon: Smile --- ## Level 4: Fundraising and Finance Pro (Module 9-11) These modules focus on preparing you to secure the resources and relationships that fuel startup growth. You’ll start by exploring best practices for engaging with VCs and designing equity incentive plans that both attract top talent and align stakeholder interests. From there, you’ll learn how to identify, apply for, and secure grants that provide vital funding to support your mission and impact. Finally, you’ll master the art of fundraising through powerful pitch decks, compelling storytelling, and proven presentation techniques that set you up for successful campaigns. Together, these modules equip you with the skills and strategies to confidently navigate the fundraising landscape and build lasting investor relationships. # Course Completion Certificate (/academy/entrepreneur/go-to-market/certificate) --- title: Course Completion Certificate updated: 2025-09-05 authors: [nicolasarnedo] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course! Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to Go-to-Market Strategies (/academy/entrepreneur/go-to-market) --- title: Welcome to Go-to-Market Strategies description: Master go-to-market strategies, sales, and pricing for your Web3 startup updated: 2025-01-31 authors: [nicolasarnedo] icon: Smile --- ## Level 2: Go-To-Market Strategist (Module 4 - 6) These modules focus on turning opportunities into tangible growth by sharpening your sales and go-to-market skills. You’ll start by learning how to generate quality leads and spot the signals that indicate real potential, ensuring your efforts are targeted where they matter most. Next, you’ll master sales and messaging techniques to craft value propositions that resonate, connect authentically with your audience, and convert leads into lasting customer relationships. Finally, you’ll develop pricing strategies that not only reflect the true value of your product but also drive sustainable growth and align with your market. Together, these modules equip you to build a sales engine that accelerates your startup’s success. # Course Completion Certificate (/academy/entrepreneur/web3-community-architect/certificate) --- title: Course Completion Certificate updated: 2025-09-05 authors: [nicolasarnedo] icon: BadgeCheck --- import CertificatePage from '@/components/quizzes/certificates'; You've made it to the end of the course! Let's check your progress and get your certificate. Thank you for participating in this course. We hope you found it informative and enjoyable! # Welcome to Web3 Community Architecture (/academy/entrepreneur/web3-community-architect) --- title: Welcome to Web3 Community Architecture description: Master the art of building and nurturing thriving Web3 communities that drive adoption and growth updated: 2025-07-31 authors: [nicolasarnedo] icon: Smile --- ## Level 3: Web3 Community Architect (Module 7 and 8) These modules are designed to help you amplify your startup’s reach, build lasting relationships, and explore innovative growth models. You’ll begin by discovering how to cultivate and leverage engaged communities that fuel loyalty, advocacy, and long-term impact. Next, you’ll learn to harness the power of social media and events strategically—strengthening your brand, deepening audience engagement, and creating momentum for growth. Finally, you’ll dive into the principles of token design, exploring economic models and distribution strategies that enable fair, effective, and sustainable ecosystems. Together, these modules equip you with the tools to grow your startup’s presence, influence, and value. # Features (/academy/avalanche-l1/avacloudapis/02-overview/01-about) --- title: Features description: Learn about the features of the AvaCloud API. updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- ## What is the Data API? The Data API provides web3 application developers with multi-chain data related to Avalanche’s primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata. ### Data API Features - **Extensive L1 Support**: Gain access to data from over 100+ L1s across both mainnet and testnet. If an L1 is listed on the [Avalanche Explorer](https://subnets.avax.network/stats/), you can query its data using the Data API. - **Transactions and UTXOs**: Easily retrieve details related to transactions, UTXOs, and token transfers from Avalanche EVMs, Ethereum, and Avalanche’s Primary Network (P-Chain, X-Chain and C-Chain). - **Blocks**: Retrieve latest blocks and block details - **Balances**: Fetch balances of native, ERC-20, ERC-721, and ERC-1155 tokens along with relevant metadata. - **Tokens**: Augment your user experience with asset details. - **Staking**: Get staking related data for active and historical validations. ## What is the Metrics API? The Metrics API equips web3 developers with a robust suite of tools to access and analyze on-chain activity across Avalanche’s primary network, Avalanche L1s, and other supported EVM chains. This API delivers comprehensive metrics and analytics, enabling you to seamlessly integrate historical data on transactions, gas consumption, throughput, staking, and more into your applications. The Metrics API, along with the Data API are the driving force behind every graph you see on the [Avalanche Explorer](https://subnets.avax.network/stats/). From transaction trends to staking insights, the visualizations and data presented are all powered by these APIs, offering real-time and historical insights that are essential for building sophisticated, data-driven blockchain products. ### Metrics API Features - **Chain Throughput**: Retrieve detailed metrics on gas consumption, Transactions Per Second (TPS), and gas prices, including rolling windows of data for granular analysis. - **Cumulative Metrics**: Access cumulative data on addresses, contracts, deployers, and transaction counts, providing insights into network growth over time. - **Staking Information**: Obtain staking-related data, including the number of validators and delegators, along with their respective weights, across different L1s. - **Blockchains and L1s**: Get information about supported blockchains, including EVM Chain IDs, blockchain IDs, and L1s associations, facilitating multi-chain analytics. - **Composite Queries**: Perform advanced queries by combining different metric types and conditions, enabling detailed and customizable data retrieval. ## What is the AvaCloudSDK? The [AvaCloud SDK](https://developers.avacloud.io/avacloud-sdk/getting-started) provides web3 application developers with multi-chain data related to Avalanche’s primary network, Avalanche L1s, and Ethereum. With the Data API, you can easily build products that leverage real-time and historical transaction and transfer history, native and token balances, and various types of token metadata. The SDK is currently available in TypeScript, with more languages coming soon. If you are interested in a language that is not listed, please reach out to us in the [`#dev-tools`](https://discord.com/login?redirect_to=%2Flogin%3Fredirect_to%3D%252Fchannels%252F578992315641626624%252F1280920394236297257) channel in the [Avalanche Discord](https://discord.com/invite/avax). # APIs vs RPCs (/academy/avalanche-l1/avacloudapis/02-overview/02-apis-vs-rpc) --- title: APIs vs RPCs description: Learn how the AvaCloud Data API differs from RPC calls updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- Blockchain RPCs and APIs both facilitate interactions with a network, but they differ significantly in how they operate. ### RPCs **Blockchain RPCs** allow you to communicate directly with a blockchain node, performing tasks like querying data or submitting transactions. These are low-level, synchronous calls, requiring a deep understanding of the blockchain's structure and specific commands. To get a more comprehensive understanding of Ethereum's JSON-RPC API, you can refer to the [official Ethereum documentation](https://ethereum.org/en/developers/docs/apis/json-rpc/). ### APIs **Blockchain APIs**, like the AvaCloud Data API, abstract away much of the complexity. They offer higher-level, user-friendly endpoints that streamline interactions, making it easier to build and manage blockchain applications without needing in-depth knowledge of the underlying blockchain protocols. To get a more comprehensive understanding of the AvaCloud Data API, you can refer to the [official AvaCloud Data API documentation](https://developers.avacloud.io/data-api/overview). ### Example Use Case For example, querying a user's ERC-20 portfolio using an RPC involves a series of complex calls to retrieve and parse raw blockchain data. Using just RPCs, you would need to: 1. Query every block on the network for transaction logs. 2. Parse each transaction log to identify ERC-20 token transfers. 3. Extract the ERC-20 token contract address. 4. For each ERC-20 token contract, query the user's address to get the balance. 5. Parse and aggregate the data to present the user's portfolio. While it may seem simple in theory, this process can be time-consuming and error-prone, especially when dealing with multiple blockchains. With the AvaCloud Data API, you could simply use a dedicated endpoint such as: ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc20 \ --header 'x-glacier-api-key: ' ``` to get a neatly formatted response with the user's ERC-20 portfolio, significantly reducing development time and complexity. ```json { "nextPageToken": "", "erc20TokenBalances": [ { "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "name": "Wrapped AVAX", "symbol": "WAVAX", "decimals": 18, "logoUri": "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/fdd6326b7a82c8388e4ee9d4be7062d4/avalanche-avax-logo.svg", "ercType": "ERC-20", "price": { "currencyCode": "usd", "value": "42.42" }, "chainId": "43114", "balance": "2000000000000000000", "balanceValue": { "currencyCode": "usd", "value": "42.42" } } ] } ``` # Data API (/academy/avalanche-l1/avacloudapis/02-overview/03-dataapi-endpoints) --- title: Data API description: Learn about AvaCloud Data API. updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/glacier-data-api-7knGxPQ6gpsJcZehfeZVYcRUAr6u1l.png) The AvaCloud Data API provides a comprehensive set of endpoints to interact with Avalanche networks. These endpoints allow you to query information about blocks, transactions, assets, and more. Below are some of the key endpoints available in the Data API. A more comprehensive list of Data API endpoints can be found [here](https://developers.avacloud.io/data-api/overview). ## Data API Reference ### EVM Endpoints - [List All Chains](https://developers.avacloud.io/data-api/evm-chains/list-chains) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains \ --header 'accept: application/json' ``` - [Get Chain Information](https://developers.avacloud.io/data-api/evm-chains/get-chain-information) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId} \ --header 'accept: application/json' ``` - [List Latest Blocks](https://developers.avacloud.io/data-api/evm-chains/list-latest-blocks) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/blocks \ --header 'accept: application/json' ``` - [Get Block Information](https://developers.avacloud.io/data-api/evm-chains/getblock) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/blocks/{blockId} \ --header 'accept: application/json' ``` - [Get Deployment Transaction](https://developers.avacloud.io/data-api/evm-transactions/get-deployment-transaction) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/contracts/{address}/transactions:getDeployment \ --header 'accept: application/json' ``` - [List Deployed Contracts](https://developers.avacloud.io/data-api/evm-transactions/list-deployed-contracts) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/contracts/{address}/deployments \ --header 'accept: application/json' ``` - [List ERC Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-transfers) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/tokens/{address}/transfers \ --header 'accept: application/json' ``` - [List Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-transactions) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions \ --header 'accept: application/json' ``` - [List Native Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-native-transactions) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listNative \ --header 'accept: application/json' ``` - [List ERC-20 Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-20-transfers) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listErc20 \ --header 'accept: application/json' ``` - [List ERC-721 Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-721-transfers) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listErc721 \ --header 'accept: application/json' ``` - [List ERC-1155 Transfers](https://developers.avacloud.io/data-api/evm-transactions/list-erc-1155-transfers) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listErc1155 \ --header 'accept: application/json' ``` - [List Internal Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-internal-transactions) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/transactions:listInternals \ --header 'accept: application/json' ``` - [Get Transaction](https://developers.avacloud.io/data-api/evm-transactions/get-transaction) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/transactions/{txHash} \ --header 'accept: application/json' ``` - [List Transactions For a Block](https://developers.avacloud.io/data-api/evm-transactions/list-transactions-for-a-block) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/blocks/{blockId}/transactions \ --header 'accept: application/json' ``` - [List Latest Transactions](https://developers.avacloud.io/data-api/evm-transactions/list-latest-transactions) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/transactions \ --header 'accept: application/json' ``` - [Get Native Token Balance](https://developers.avacloud.io/data-api/evm-balances/get-native-token-balance) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:getNative \ --header 'accept: application/json' ``` - [List ERC-20 Balances](https://developers.avacloud.io/data-api/evm-balances/list-erc-20-balances) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc20 \ --header 'accept: application/json' ``` - [List ERC-721 Balances](https://developers.avacloud.io/data-api/evm-balances/list-erc-721-balances) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc721 \ --header 'accept: application/json' ``` - [List ERC-1155 Balances](https://developers.avacloud.io/data-api/evm-balances/list-erc-1155-balances) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listErc1155 \ --header 'accept: application/json' ``` - [List Collectible (ERC-721 and ERC-1155) Balances](https://developers.avacloud.io/data-api/evm-balances/list-collectible-erc-721erc-1155-balances) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address}/balances:listCollectibles \ --header 'accept: application/json' ``` - [Get Contract Metadata](https://developers.avacloud.io/data-api/evm-contracts/get-contract-metadata) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/addresses/{address} \ --header 'accept: application/json' ``` - [Reindex NFT Metadata](https://developers.avacloud.io/data-api/nfts/reindex-nft-metadata) ```bash curl --request POST \ --url https://glacier-api.avax.network/v1/chains/{chainId}/nfts/collections/{address}/tokens/tokenId:reindex \ --header 'accept: application/json' ``` - [List Tokens](https://developers.avacloud.io/data-api/nfts/list-tokens) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/nfts/collections/{address}/tokens \ --header 'accept: application/json' ``` - [Get Token Details](https://developers.avacloud.io/data-api/nfts/get-token-details) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/chains/{chainId}/nfts/collections/{address}/tokens/tokenId \ --header 'accept: application/json' ``` ### Avalanche Primary Network Endpoints - [Get Chain Interactions for Addresses](https://developers.avacloud.io/data-api/primary-network/get-chain-interactions-for-addresses) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/{network}/addresses:listChainIds \ --header 'accept: application/json' ``` - [Get Network Details](https://developers.avacloud.io/data-api/primary-network/get-network-details) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/{network} \ --header 'accept: application/json' ``` - [List Blockchains](https://developers.avacloud.io/data-api/primary-network/list-blockchains) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/{network}/blockchains \ --header 'accept: application/json' ``` - [List Subnets](https://developers.avacloud.io/data-api/primary-network/list-subnets) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/subnets \ --header 'accept: application/json' ``` - [Get Subnet Details by ID](https://developers.avacloud.io/data-api/primary-network/get-subnet-details-by-id) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/subnets/{subnetId} \ --header 'accept: application/json' ``` - [List Validators](https://developers.avacloud.io/data-api/primary-network/list-validators) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/validators \ --header 'accept: application/json' ``` - [Get Single Validator Details](https://developers.avacloud.io/data-api/primary-network/get-single-validator-details) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/validators/{nodeId} \ --header 'accept: application/json' ``` - [List Delegators](https://developers.avacloud.io/data-api/primary-network/list-delegators) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/delegators \ --header 'accept: application/json' ``` - [Get Block](https://developers.avacloud.io/data-api/primary-network-blocks/get-block) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/blocks/{blockId} \ --header 'accept: application/json' ``` - [List Blocks Proposed by Node](https://developers.avacloud.io/data-api/primary-network-blocks/list-blocks-proposed-by-node) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/nodes/{nodeId}/blocks \ --header 'accept: application/json' ``` - [List Latest Blocks](https://developers.avacloud.io/data-api/primary-network-blocks/list-latest-blocks) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/blocks \ --header 'accept: application/json' ``` - [List Vertices](https://developers.avacloud.io/data-api/primary-network-vertices/list-vertices) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/vertices \ --header 'accept: application/json' ``` - [Get Vertex](https://developers.avacloud.io/data-api/primary-network-vertices/get-vertex) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/vertices/vertexHash \ --header 'accept: application/json' ``` - [List Vertices by Height](https://developers.avacloud.io/data-api/primary-network-vertices/list-vertices-by-height) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/vertices:listByHeight \ --header 'accept: application/json' ``` - [Get Transaction](https://developers.avacloud.io/data-api/primary-network-transactions/get-transaction) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/{network}/blockchains/{blockchainId}/transactions/{txHash} \ --header 'accept: application/json' ``` - [List Latest Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-latest-transactions) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/{network}/blockchains/{blockchainId}/transactions \ --header 'accept: application/json' ``` - [List Staking Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-staking-transactions) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/transactions:listStaking \ --header 'accept: application/json' ``` - [List Asset Transactions](https://developers.avacloud.io/data-api/primary-network-transactions/list-asset-transactions) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/assets/{assetId}/transactions \ --header 'accept: application/json' ``` - [List UTXOs](https://developers.avacloud.io/data-api/primary-network-utxos/list-utxos) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/utxos \ --header 'accept: application/json' ``` - [Get Balances](https://developers.avacloud.io/data-api/primary-network-balances/get-balances) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/11111111111111111111111111111111LpoYY/balances \ --header 'accept: application/json' ``` - [List Pending Rewards](https://developers.avacloud.io/data-api/primary-network-rewards/list-pending-rewards) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/rewards:listPending \ --header 'accept: application/json' ``` - [List Historical Rewards](https://developers.avacloud.io/data-api/primary-network-rewards/list-historical-rewards) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/rewards \ --header 'accept: application/json' ``` - [Get Asset Details](https://developers.avacloud.io/data-api/primary-network/get-asset-details) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/networks/mainnet/blockchains/2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM/assets/{assetId} \ --header 'accept: application/json' ``` # Webhooks API (/academy/avalanche-l1/avacloudapis/02-overview/04-webhooks) --- title: Webhooks API description: Learn about AvaCloud Webhooks API. updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- ## What is a Webhook? A webhook is a communication mechanism to provide applications with real-time information. It delivers data to other applications as it happens, meaning you get data immediately, unlike typical APIs where you would need to poll for data to get it in "real-time". This makes webhooks much more efficient for both providers and consumers. Webhooks work by registering a URL to send notifications once certain events occur. You can create receiver endpoints in your server in any programming language and those will have an associated URL. This object contains all the relevant information about what just happened, including the type of event and the data associated with that event. ## What are AvaCloud Webhooks? With the Webhooks API, you can monitor real-time events on the Avalanche C-Chain and L1s. For example, you can monitor smart contract events, track NFT transfers, and observe wallet-to-wallet transactions. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/glacier-webhooks-xpLeH8o7slcnvOdWsIorDJSUeNlz04.png) ### Key Features: - **Real-time notifications:** Receive immediate updates on specified on-chain activities without polling. - **Customizable:** Specify the desired event type to listen for, customizing notifications according to individual requirements. - **Secure:** Employ shared secrets and signature-based verification to guarantee that notifications originate from a trusted source. - **Broad Coverage:** Support for C-chain mainnet, testnet, and L1s within the Avalanche ecosystem, ensuring wide-ranging monitoring capabilities. ### Use Cases: - **NFT Marketplace Transactions:** Get alerts for NFT minting, transfers, auctions, bids, sales, and other interactions within NFT marketplaces. - **Wallet Notifications:** Receive alerts when an address performs actions such as sending, receiving, swapping, or burning assets. - **DeFi Activities:** Receive notifications for various DeFi activities such as liquidity provisioning, yield farming, borrowing, lending, and liquidations. ## Webhooks API Reference [Create a new webhook](https://developers.avacloud.io/webhooks-api/webhooks/create-a-webhook) ```bash curl --request POST \ --url https://glacier-api.avax.network/v1/webhooks \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' { "eventType": "address_activity" } ``` [Lists webhooks for the user.](https://developers.avacloud.io/webhooks-api/webhooks/list-webhooks) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/webhooks \ --header 'accept: application/json' ``` [Retrieves a webhook by ID.](https://developers.avacloud.io/webhooks-api/webhooks/get-a-webhook-by-id) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/webhooks/id \ --header 'accept: application/json' ``` [Deactivates a webhook by ID.](https://developers.avacloud.io/webhooks-api/webhooks/deactivate-a-webhook) ```bash curl --request DELETE \ --url https://glacier-api.avax.network/v1/webhooks/id \ --header 'accept: application/json' ``` [Updates an existing webhook.](https://developers.avacloud.io/webhooks-api/webhooks/update-a-webhook) ```bash curl --request PATCH \ --url https://glacier-api.avax.network/v1/webhooks/id \ --header 'accept: application/json' \ --header 'content-type: application/json' ``` [Generates a new shared secret.](https://developers.avacloud.io/webhooks-api/webhooks/generate-a-shared-secret) ```bash curl --request POST \ --url https://glacier-api.avax.network/v1/webhooks:generateOrRotateSharedSecret \ --header 'accept: application/json' ``` [Get a previously generated shared secret.](https://developers.avacloud.io/webhooks-api/webhooks/get-a-shared-secret) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/webhooks:getSharedSecret \ --header 'accept: application/json' ``` [Add addresses to webhook.](https://developers.avacloud.io/webhooks-api/webhooks/add-addresses-to-webhook) ```bash curl --request PATCH \ --url https://glacier-api.avax.network/v1/webhooks/id/addresses \ --header 'accept: application/json' \ --header 'content-type: application/json' ``` [Remove addresses from webhook.](https://developers.avacloud.io/webhooks-api/webhooks/remove-addresses-from-webhook) ```bash curl --request DELETE \ --url https://glacier-api.avax.network/v1/webhooks/id/addresses \ --header 'accept: application/json' \ --header 'content-type: application/json' ``` [List adresses by webhook.](https://developers.avacloud.io/webhooks-api/webhooks/list-adresses-by-webhook) ```bash curl --request GET \ --url https://glacier-api.avax.network/v1/webhooks/id/addresses \ --header 'accept: application/json' ``` # Create API Key (/academy/avalanche-l1/avacloudapis/03-environment-setup/01-avacloud-account) --- title: Create API Key description: Get an API key for AvaCloud updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; ## Get an AvaCloud API Key In order to utilize your accounts rate limits, you will need to make API requests with an API key. You can generate API Keys from the [AvaCloud portal](https://app.avacloud.io/glacier-api/). Create an account on [AvaCloud](https://avacloud.io). Click on the `Web3 Data API` [tab](https://app.avacloud.io/glacier-api/). ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/create-api-key-gfBR9O5rSJilgNzws8JWyHXctbsXox.png) Click `+ Add API Key` and create a new name for your API key. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/name-api-key-NuNRHpnSFC3dLvLYdbztDTlAf8Ax6k.png) **Save your API key** in a secure location. We will need it in the next step. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/grab-api-key-KN2CdMQVWrHPKjPY4vxS4H6pIZVJmW.png) After generating the API keys the AvaCloud page should look like this: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/final-api-key-5ydE6hNLZajRH3hNEAYE5IFFmNBdPX.png) Once you've created and retrieved the key, you will be able to make authenticated queries by passing in your API key in the `x-glacier-api-key` header of your HTTP request. An example curl request to the Data API: ```bash curl -H "Content-Type: Application/json" -H "x-glacier-api-key: " \ "https://glacier-api.avax.network/v1/chains" ``` # Setup AvaCloudSDK Starter Kit (/academy/avalanche-l1/avacloudapis/03-environment-setup/02-setup-starter-kit) --- title: Setup AvaCloudSDK Starter Kit description: Set up the AvaCloudSDK Starter Kit in a GitHub Codespace updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- import Link from 'next/link'; import { cn } from '@/utils/cn'; import { buttonVariants } from '@/components/ui/button.tsx' import { Step, Steps } from 'fumadocs-ui/components/steps'; In this course we will run the AvaCloudSDK Starter Kit in a Github Codepsace. This is the quickest way to get started. The `AvaCloudSDK Starter Kit` contains everything we need to get started quickly with the AvaCloud API. ### Open the AvaCloudSDK Starter Kit Github Repository: **Make sure you are on the `follow-along` branch!** Open AvaCloudSDK Starter Kit ### Create a Codespace [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=follow-along&repo=851861669&machine=standardLinux32gb) The Codespace will open in a new tab. Wait a few minutes until it's fully built. ### Verify everything is working correctly Open the terminal with `` Ctrl + ` `` or by opening it through the menu: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/terminal-08TThSFqM438J1Jd5E1bh1OyMYAXWg.png) ### Set the `AVACLOUD_API_KEY` environment variable Create a `.env` file in the root of the project and add your API key from [the previous step](/academy/avacloudapis/03-environment-setup/01-avacloud-account): ```bash AVACLOUD_API_KEY=ac_rGIKESl9_9DWuLfJJQLSV5nlzbKR7eHxym6XW3XEQJeNBDRxI... ``` ### Start the NextJS app Now we can run the NextJS app in hot reload mode: ```bash yarn dev ``` It should preview within the Codespace: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/site-running-neUxhhADHqXs34Jrgw1uSJhqokItAC.png) ### Optional: Open Codespace locally in Visual Studio Code You can switch any time from the browser IDE to Visual Studio Code: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/vs-code-DNVuxWt4Z4ffoDsszoGDyUuhXvhC8m.png) The first time you switch, you will be asked to install the [Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces) and connect VS Code to you GitHub account, if it is not already connceted. # Time to Build! (/academy/avalanche-l1/avacloudapis/03-environment-setup/03-what-we-build) --- title: Time to Build! description: Learn about what we will build in this course updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- ### Finished Setup Once your environment is set up, we can start building some applications using the AvaCloud API and the AvaCloudSDK. The home page should look like this: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/preview-L1zktfNDP4fWzdESdhIQ7KxsfnQJj0.png) ### What We Will Build Here is a brief overview of what we will be building in this course: - ERC-20 Balance App - Wallet Portfolio App - Block Explorer App Each of these applications will utilize the Data API to get data from the Avalanche network. We will fetch data such as ERC-20 token balances, NFT balances, recent transactions from an address, block information and much more. # Overview (/academy/avalanche-l1/avacloudapis/04-erc20-token-balance-app/01-overview) --- title: Overview description: Use the AvaCloud Data API to create a simple web app that displays a user's ERC-20 token balances. updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- In this section we will use the Data API to create a simple web app that displays a user's ERC-20 token portfolio. This app will allow users to input their Avalanche C-Chain address and view a list of their ERC-20 token balances. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/balance-app-9ggCriiHBsggku1bFGF8tL1lyThSx4.png) We will use two endpoints from the Data API to accomplish this: - [`data.evm.blocks.getLatestBlocks`](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) - [`data.evm.balances.listErc20Balances`](https://developers.avacloud.io/data-api/evm-balances/list-erc-20-balances) # Understanding the Code (/academy/avalanche-l1/avacloudapis/04-erc20-token-balance-app/02-understanding-code) --- title: Understanding the Code description: Before we start coding, let's take a look at the code we will be working with. updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; There will be two main files that we will be working with in this section. ### `Page.tsx` This is the code that will be rendered on the client side, as distinguished by `"use client";` at the top of the file. It contains the React components that will be displayed to the user and is responsible for making the API calls to our backend, which in turn calls the Data API. It is important to understand that when you `"use client"` in a NextJS project, it will be rendered on the client side. This means that the code will be executed in the user's browser and not on the server. This is important to keep in mind when working with sensitive data or when you want to keep your API keys secure. Besides this, we have two main functions that we will be working with in this file: ```tsx title="src/app/balance-app/page.tsx" const handleSetAddress = async () => { // // TODO: Implement handleSetAddress // }; ``` `handleSetAddress` is a simple function that will be called when the user clicks the "Set Address" button. It will ensure the address is valid then set the inputted address to the React State. Next, we call our next function `fetchERC20Balances` to get the user's balance. ```tsx title="src/app/balance-app/page.tsx" const fetchERC20Balances = async (address: string) => { // // TODO: Implement fetchERC20Balances // }; ``` `fetchERC20Balances` is a function that will make a call to our backend to get the user's ERC-20 token balances. It will first get the current block height, then call the `listErc20Balances` method on our backend with the user's address and the block height. It will then return the balances as an array of `Erc20TokenBalance` objects. ### `Route.ts` This code will be executed on the server side, as distinguished by `"use server";` at the top of the file. It contains the code that will be executed on the server side and is responsible for making the API calls to the Data API. There are a few key components to understand in this file: ```tsx title="src/app/api/balance/route.ts" import { AvaCloudSDK } from "@avalabs/avacloud-sdk"; const avaCloudSDK = new AvaCloudSDK({ apiKey: process.env.AVACLOUD_API_KEY, chainId: "43114", // Avalanche Mainnet network: "mainnet", }); ``` Here we initialize the `AvaCloudSDK` with our AvaCloud API key and the chainId of `43114` for the Avalanche Mainnet. This will allow us to make calls to the Data API. ```tsx title="src/app/api/balance/route.ts" export async function GET(request: Request) { const { searchParams } = new URL(request.url) const method = searchParams.get('method') try { let result switch (method) { case 'getBlockHeight': result = await getBlockHeight() break case 'listErc20Balances': const address: string = searchParams.get('address')! const blockNumber: string = searchParams.get('blockNumber')! result = await listErc20Balances(address, blockNumber); break default: return NextResponse.json({ error: 'Invalid method' }, { status: 400 }) } return NextResponse.json(result) } catch (error) { return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }) } } ``` Here we define the internal API methods for our backend. We have two methods that we will be working with in this section: `getBlockHeight` and `listErc20Balances`. We create both of these methods internally, then forward the request to the Data API. We then return the result to the client. ```tsx title="src/app/api/balance/route.ts" async function getBlockHeight() { // // TODO: Implement getBlockHeight // } async function listErc20Balances(address: string, blockNumber: string) { // // TODO: Implement listErc20Balances // } ``` In the next section, we will implement the `listErc20Balances` and `getBlockHeight` function to call the Data API through the AvaCloudSDK. # Modifying the Code (/academy/avalanche-l1/avacloudapis/04-erc20-token-balance-app/03-modifying-code) --- title: Modifying the Code description: Lets modify the code to implement the Data API. updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; In this section we will modify the code to implement the Data API. ### Modify Backend `src/app/api/balance/route.ts` First we will implement the `getBlockHeight` function. The goal of this function is to fetch recent blocks from the Data API then to return the highest block in the first position. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) to see how to fetch the latest blocks. ```tsx title="src/app/api/balance/route.ts" async function getBlockHeight() { const result = await avaCloudSDK.data.evm.blocks.getLatestBlocks({ pageSize: 1, }); return result.result.blocks[0].blockNumber } ``` Next we will implement the `listErc20Balances` function. The goal of this function is to fetch the ERC-20 token balances for a given address at a specific block height. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-balances/list-erc-20-balances) to see how to fetch ERC-20 token balances. Note the `Erc20TokenBalance` type that is imported, we should use this to combine paged results. ```tsx title="src/app/api/balance/route.ts" async function listErc20Balances(address: string, blockNumber: string) { const result = await avaCloudSDK.data.evm.balances.listErc20Balances({ blockNumber: blockNumber, pageSize: 10, address: address, }); const balances: Erc20TokenBalance[] = []; for await (const page of result) { balances.push(...page.result.erc20TokenBalances); } return balances } ``` ### Modify Frontend `src/app/balance-app/page.tsx` First we will implement the `fetchERC20Balances` function. The goal of this function is to make a call to our backend to get the user's ERC-20 token balances. Make a call to our backend first for the most recent block height, then our `listErc20Balances` API. Finally return the results. ```tsx title="src/app/balance-app/page.tsx" const fetchERC20Balances = async (address: string) => { const blockResult = await fetch("api/balance?method=getBlockHeight"); const blockNumber = await blockResult.json(); const balanceResult = await fetch("api/balance?method=listErc20Balances&address=" + address + "&blockNumber=" + blockNumber); const balances = await balanceResult.json(); return balances as Erc20TokenBalance[]; }; ``` Next we will implement the `handleSetAddress` function. The goal of this function is to set the address in the state and fetch the ERC-20 token balances for that address using our `fetchERC20Balances` function. First make sure the address is valid, then update the state for `Address` and `Balances` ```tsx title="src/app/balance-app/page.tsx" const handleSetAddress = async () => { const addressInput = document.getElementById("address") as HTMLInputElement; const address = addressInput.value; const addressPattern = /^0x[a-fA-F0-9]{40}$/; if (addressInput && addressPattern.test(address)) { setAddress(address); setBalances(await fetchERC20Balances(address)); } }; ``` # Final Result (/academy/avalanche-l1/avacloudapis/04-erc20-token-balance-app/04-final) --- title: Final Result description: The final result of the ERC-20 token balance app. updated: 2024-09-03 authors: [owenwahlgren] icon: Book --- If we implemented the code correctly, we should have a working ERC-20 token balance app that displays the user's token balances. The app will look like the following: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/balance-app-init-bIVbTrMxCmFteBLTTcDRHGuVU75Kcd.png) After setting the address field, the app will display the user's ERC-20 token balances: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/balance-app-9ggCriiHBsggku1bFGF8tL1lyThSx4.png) Notice how the app also displays images for each token. This is done by fetching the token metadata from `listErc20Balances` and displaying the token's logo. # Overview (/academy/avalanche-l1/avacloudapis/05-wallet-portfolio-app/01-overview) --- title: Overview description: Use the Data API to create a simple web app that displays data and metrics on a connected wallet. updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- In this section we will expand on the ERC-20 Balance App with NFTs (ERC-721) and ERC-1155 tokens. We will also add a connect wallet button to allow users to connect their own wallet and view their own token balances. Here is a preview of what we will build: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/nfts-v4LwE4Ij450GkYThM5JQG7ghKtT1LQ.png) ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/tokens-ILhHQF5XpddcXcg3pbyG6aQ8hbd9XJ.png) We will use a few additional endpoints from the Data API to accomplish this: - [`data.evm.balances.listErc721Balances`](https://developers.avacloud.io/data-api/evm-balances/list-erc-721-balances) - [`data.evm.balances.listErc1155Balances`](https://developers.avacloud.io/data-api/evm-balances/list-erc-1155-balances) - [`data.evm.transactions.listTransactions`](https://developers.avacloud.io/data-api/evm-transactions/list-transactions) # Understanding the Code (/academy/avalanche-l1/avacloudapis/05-wallet-portfolio-app/02-understanding-code) --- title: Understanding the Code description: Before we start coding, let's take a look at the code we will be working with. updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; There will be two main files that we will be working with in this section. ### `Page.tsx` This is the code that will be rendered on the client side, as distinguished by `"use client";` at the top of the file. It contains the React components that will be displayed to the user and is responsible for making the API calls to our backend, which in turn calls the Data API. It is important to understand that when you `"use client"` in a NextJS project, it will be rendered on the client side. This means that the code will be executed in the user's browser and not on the server. This is important to keep in mind when working with sensitive data or when you want to keep your API keys secure. Besides this, we have three main functions that we will be working with in this file: ```tsx title="src/app/basic-wallet/page.tsx" const fetchERC721Balances = async (address: string) => { // // TODO: Implement this! // return [] as Erc721TokenBalance[]; } ``` `fetchERC721Balances` is a function that will make a call to our backend to get the user's ERC-721 token balances. It will call the `listErc721Balances` method on our backend with the user's address. It will then return the balances as an array of `Erc721TokenBalance` objects. ```tsx title="src/app/basic-wallet/page.tsx" const fetchERC1155Balances = async (address: string) => { // // TODO: Implement this! // return [] as Erc1155TokenBalance[]; } ``` `fetchERC1155Balances` is a function that will make a call to our backend to get the user's ERC-1155 token balances. It will call the `listErc1155Balances` method on our backend with the user's address. It will then return the balances as an array of `Erc1155TokenBalance` objects. ```tsx title="src/app/basic-wallet/page.tsx" const fetchRecentTransactions = async (address: string) => { // // TODO: Implement this! // return {} as TransactionDetails; } ``` `fetchRecentTransactions` is a function that will make a call to our backend to get the user's recent transactions for all tokens. It will call the `listRecentTransactions` method on our backend with the user's address. It will then return the balances as an object of type `TransactionDetails`. ### `Route.ts` This code will be executed on the server side, as distinguished by `"use server";` at the top of the file. It contains the code that will be executed on the server side and is responsible for making the API calls to the Data API. There are a few key components to understand in this file: ```tsx title="src/app/api/wallet/route.ts" import { AvaCloudSDK } from "@avalabs/avacloud-sdk"; const avaCloudSDK = new AvaCloudSDK({ apiKey: process.env.AVACLOUD_API_KEY, chainId: "43114", // Avalanche Mainnet network: "mainnet", }); ``` Here we initialize the `AvaCloudSDK` with our AvaCloud API key and the chainId of `43114` for the Avalanche Mainnet. This will allow us to make calls to the Data API. ```tsx title="src/app/api/wallet/route.ts" export async function GET(request: Request) { const { searchParams } = new URL(request.url) const method = searchParams.get('method') let address try { let result switch (method) { case 'listERC721Balances': address = searchParams.get('address')! result = await listERC721Balances(address) break case 'listERC1155Balances': address = searchParams.get('address')! result = await listErc1155Balances(address) break case 'listRecentTransactions': address = searchParams.get('address')! result = await listRecentTransactions(address) break default: return NextResponse.json({ error: 'Invalid method' }, { status: 400 }) } return NextResponse.json(result) } catch (error) { return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }) } } ``` Here we define the internal API methods for our backend. We have two methods that we will be working with in this section: `getBlockHeight`, `listERC721Balances`, `listErc1155Balances` and `listRecentTransactions`. We create all of these methods internally, then forward the request to the Data API. We then return the result to the client. ```tsx title="src/app/api/wallet/route.ts" async function getBlockHeight() { // // TODO: Implement this! // return } const listERC721Balances = async (address: string) => { // // TODO: Implement this! // return } const listErc1155Balances = async (address: string) => { // // TODO: Implement this! // return } const listRecentTransactions = async (address: string) => { // // TODO: Implement this! // return } ``` In the next section, we will implement these functions to call the Data API through the AvaCloudSDK. # Modifying the Code (/academy/avalanche-l1/avacloudapis/05-wallet-portfolio-app/03-modifying-code) --- title: Modifying the Code description: Lets modify the code to implement the Data API. updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; In this section, we will modify the code to implement the Data API. ### Modify Backend `src/app/api/wallet/route.ts` First we will implement the `getBlockHeight` function. This function is the same as the one in ERC20 Balance App, but we will repeat it for the sake of the tutorial. The goal of this function is to fetch recent blocks from the Data API then to return the highest block in the first position. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) to see how to fetch the latest blocks. ```tsx title="src/app/api/wallet/route.ts" async function getBlockHeight() { const result = await avaCloudSDK.data.evm.blocks.getLatestBlocks({ pageSize: 1, }); return result.result.blocks[0].blockNumber } ``` Next we will implement the `listERC721Balances` function. The goal of this function is to fetch the ERC-721 token balances for a given address. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-balances/list-erc-721-balances) to see how to fetch ERC-721 token balances. Note the `Erc721TokenBalance` type that is imported, we should use this to combine paged results. ```tsx title="src/app/api/wallet/route.ts" const listERC721Balances = async (address: string) => { const result = await avaCloudSDK.data.evm.balances.listErc721Balances({ pageSize: 10, address: address, }); const balances: Erc721TokenBalance[] = []; for await (const page of result) { balances.push(...page.result.erc721TokenBalances); } return balances } ``` Now we will implement the `listErc1155Balances` function. The goal of this function is to fetch the ERC-1155 token balances for a given address. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-balances/list-erc-1155-balances) to see how to fetch ERC-1155 token balances. Note the `Erc1155TokenBalance` type that is imported, we should use this to combine paged results. ```tsx title="src/app/api/wallet/route.ts" const listErc1155Balances = async (address: string) => { const result = await avaCloudSDK.data.evm.balances.listErc1155Balances({ pageSize: 10, address: address, }); const balances: Erc1155TokenBalance[] = []; for await (const page of result) { balances.push(...page.result.erc1155TokenBalances); } return balances } ``` Finally we will implement the `listRecentTransactions` function. The goal of this function is to fetch recent transactions for a given address given a block start and end. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-transactions/list-transactions) to see how to fetch recent transactions. Note the `TransactionDetails` type that is imported, we should use this to combine and sort paged results. ```tsx title="src/app/api/wallet/route.ts" const listRecentTransactions = async (address: string) => { const blockHeight = await getBlockHeight() const result = await avaCloudSDK.data.evm.transactions.listTransactions({ pageSize: 10, startBlock: blockHeight - 100000, endBlock: blockHeight, address: address, sortOrder: "desc", }); const transactions: TransactionDetails = { erc20Transfers: [], erc721Transfers: [], erc1155Transfers: [], nativeTransaction: { blockNumber: '', blockTimestamp: 0, blockHash: '', blockIndex: 0, txHash: '', txStatus: '', txType: 0, gasLimit: '', gasUsed: '', gasPrice: '', nonce: '', from: { name: undefined, symbol: undefined, decimals: undefined, logoUri: undefined, address: '' }, to: { name: undefined, symbol: undefined, decimals: undefined, logoUri: undefined, address: '' }, value: '' }, } for await (const page of result) { for (const transaction of page.result.transactions) { if (transaction.erc20Transfers) { if (transactions.erc20Transfers) { transactions.erc20Transfers.push(...transaction.erc20Transfers); } } else if (transaction.erc721Transfers) { if (transactions.erc721Transfers) { transactions.erc721Transfers.push(...transaction.erc721Transfers); } } else if (transaction.erc1155Transfers) { if (transactions.erc1155Transfers) { transactions.erc1155Transfers.push(...transaction.erc1155Transfers); } } } } return transactions } ``` ### Modify Frontend `src/app/basic-wallet/page.tsx` Now we will modify the frontend to make calls to our backend. First we will implement the `fetchERC20Balances` function. The goal of this function is to make a call to our backend to get the user's ERC-20 token balances. Make a call to our backend first for the most recent block height, then our `listErc20Balances` API. Finally return the results. ```tsx title="src/app/basic-wallet/page.tsx" const fetchERC20Balances = async (address: string) => { const blockResult = await fetch("api/balance?method=getBlockHeight"); const blockNumber = await blockResult.json(); const balanceResult = await fetch("api/balance?method=listErc20Balances&address=" + address + "&blockNumber=" + blockNumber); const balances = await balanceResult.json(); return balances as Erc20TokenBalance[]; }; ``` Next we will implement the `fetchERC721Balances` function. The goal of this function is to call our `listErc721Balances` function on the backend and return it as a `Erc721TokenBalance` array. Make a call to our backend then parse the result as json. Return it as `Erc721TokenBalance[]`. ```tsx title="src/app/basic-wallet/page.tsx" const fetchERC721Balances = async (address: string) => { const result = await fetch(`api/wallet?method=listERC721Balances&address=${address}`); const balances = await result.json(); return balances as Erc721TokenBalance[]; } ``` Now we will implement the `fetchERC1155Balances` function. The goal of this function is to call our `listERC1155Balances` function on the backend and return it as a `Erc1155TokenBalance` array. Make a call to our backend then parse the result as json. Return it as `Erc1155TokenBalance[]`. ```tsx title="src/app/basic-wallet/page.tsx" const fetchERC1155Balances = async (address: string) => { const result = await fetch(`api/wallet?method=listERC1155Balances&address=${address}`); const balances = await result.json(); return balances as Erc1155TokenBalance[]; } ``` Finally we will implement the `fetchRecentTransactions` function. The goal of this function is to call our `listRecentTransactions` function on the backend and return it as an object of type `TransactionDetails`. Make a call to our backend then parse the result as json. Return it as type of `TransactionDetails`. ```tsx title="src/app/basic-wallet/page.tsx" const fetchRecentTransactions = async (address: string) => { const result = await fetch(`api/wallet?method=listRecentTransactions&address=${address}`); const transactions = await result.json(); return transactions as TransactionDetails; } ``` # Overview (/academy/avalanche-l1/avacloudapis/06-block-explorer-app/01-overview) --- title: Overview description: Use the Data API to create a simple block explorer. updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- In this section we will build a basic block explorer using the Data API. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avacloudsdk/explorer-Uahzju9LouXAGd61AQvm38Vxtq4cG5.png) We will use a couple additional endpoints from the Data API to accomplish this: - [`data.evm.blocks.getBlockHeight`](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) - [`data.evm.transactions.listLatestTransactions`](https://developers.avacloud.io/data-api/evm-transactions/list-latest-transactions) # Understanding the Code (/academy/avalanche-l1/avacloudapis/06-block-explorer-app/02-understanding-code) --- title: Understanding the Code description: Before we start coding, let's take a look at the code we will be working with. updated: 2024-10-09 authors: [owenwahlgren] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; There will be two main files that we will be working with in this section. ### `Page.tsx` This is the code that will be rendered on the client side, as distinguished by `"use client";` at the top of the file. It contains the React components that will be displayed to the user and is responsible for making the API calls to our backend, which in turn calls the Data API. It is important to understand that when you `"use client"` in a NextJS project, it will be rendered on the client side. This means that the code will be executed in the user's browser and not on the server. This is important to keep in mind when working with sensitive data or when you want to keep your API keys secure. Besides this, we have three main functions that we will be working with in this file: ```tsx title="src/app/basic-explorer/page.tsx" const fetchRecentTransactions = async () => { // // TODO: Implement this! // return data as NativeTransaction[] } ``` `fetchRecentTransactions` is a function that will make a call to our backend to get the most recent transactions from the chain. It will call the `getRecentTransactions` method on our backend. It will then return the transactions as an array of `NativeTransaction` objects. ```tsx title="src/app/basic-explorer/page.tsx" const fetchRecentBlocks = async () => { // // TODO: Implement this! // return data as EvmBlock[] } ``` `fetchRecentBlocks` is a function that will make a call to our backend to get the most recent blocks from the chain. It will call the `getRecentBlocks` method on our backend. It will then return the blocks as an array of `EvmBlock` objects. ### `Route.ts` This code will be executed on the server side, as distinguished by `"use server";` at the top of the file. It contains the code that will be executed on the server side and is responsible for making the API calls to the Data API. There are a few key components to understand in this file: ```tsx title="src/app/api/explorer/route.ts" import { AvaCloudSDK } from "@avalabs/avacloud-sdk"; const avaCloudSDK = new AvaCloudSDK({ apiKey: process.env.AVACLOUD_API_KEY, chainId: "43114", // Avalanche Mainnet network: "mainnet", }); ``` Here we initialize the `AvaCloudSDK` with our AvaCloud API key and the chainId of `43114` for the Avalanche Mainnet. This will allow us to make calls to the Data API. ```tsx title="src/app/api/explorer/route.ts" export async function GET(request: Request) { const { searchParams } = new URL(request.url) const method = searchParams.get('method') try { let result switch (method) { case 'getRecentTransactions': result = await getRecentTransactions() break case 'getRecentBlocks': result = await getRecentBlocks() break default: return NextResponse.json({ error: 'Invalid method' }, { status: 400 }) } return NextResponse.json(result) } catch (error) { return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }) } } ``` Here we define the internal API methods for our backend. We have two methods that we will be working with in this section: `getRecentBlocks` and `getRecentTransactions`. We create all of these methods internally, then forward the request to the Data API. We then return the result to the client. ```tsx title="src/app/api/explorer/route.ts" const getRecentBlocks = async () => { // // TODO: Implement this! // } const getRecentTransactions = async () => { // // TODO: Implement this! // } ``` In the next section, we will implement these functions to call the Data API through the AvaCloudSDK. # Modifying the Code (/academy/avalanche-l1/avacloudapis/06-block-explorer-app/03-modifying-code) --- title: Modifying the Code description: Lets modify the code to implement the Data API. updated: 2024-10-09 authors: [owenwahlgren] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; In this section we will modify the code to implement the Data API. ### Modify Backend `src/app/api/explorer/route.ts` First we will implement the `getRecentBlocks` function. The goal of this function is to fetch recent blocks from the Data API with all its information. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-blocks/list-latest-blocks) to see how to fetch the latest blocks. ```tsx title="src/app/api/explorer/route.ts" const getRecentBlocks = async () => { const result = await avaCloudSDK.data.evm.blocks.getLatestBlocks({ pageSize: 1, }); let count = 0; const blocks: EvmBlock[] = []; for await (const page of result) { if (count === 20) { break; } blocks.push(...page.result.blocks); count++; } return blocks } ``` Next we will implement the `getRecentTransactions` function. The goal of this function is to fetch all native token transfers from the Data API. Reference the [AvaCloud SDK documentation](https://developers.avacloud.io/data-api/evm-transactions/list-latest-transactions) to see how to fetch latest transactions. Note the `NativeTransaction` type that is imported, we should use this to combine paged results. ```tsx title="src/app/api/explorer/route.ts" const getRecentTransactions = async () => { const result = await avaCloudSDK.data.evm.transactions.listLatestTransactions({ pageSize: 3, }); let count = 0; const transactions: NativeTransaction[] = []; for await (const page of result) { if (count === 20) { break; } transactions.push(...page.result.transactions); count++; } return transactions; } ``` ### Modify Frontend `src/app/basic-explorer/page.tsx` First we will implement the `fetchRecentTransactions` function. The goal of this function is to make a call to our backend to get recent transactions. Make a call to our backend first for our `getRecentTransactions` method. Finally return the results. ```tsx title="src/app/basic-explorer/page.tsx" const fetchRecentTransactions = async () => { const response = await fetch(`/api/explorer?method=getRecentTransactions`) const data = await response.json() return data as NativeTransaction[] } ``` Next we will implement the `fetchRecentBlocks` function. The goal of this function is to make a call to our backend to get recent block information. Make a call to our backend first for our `getRecentBlocks` method. Finally return the results. ```tsx title="src/app/basic-explorer/page.tsx" const fetchRecentBlocks = async () => { const response = await fetch(`/api/explorer?method=getRecentBlocks`) const data = await response.json() return data as EvmBlock[] } ``` # Overview (/academy/avalanche-l1/avacloudapis/07-using-webhooks/01-overview) --- title: Overview description: Coming soon! updated: 2024-09-13 authors: [owenwahlgren] icon: Book --- # Avalanche Consensus (/academy/avalanche-l1/avalanche-fundamentals/02-avalanche-consensus-intro/01-avalanche-consensus-intro) --- title: Avalanche Consensus description: Learn how blockchains arrive at consensus using different mechanisms. updated: 2024-05-31 authors: [martineckardt] icon: Book --- Avalanche Consensus is a novel consensus protocol that is used in the Avalanche network. It is a leaderless, decentralized, and scalable consensus protocol that is used to achieve consensus on the state of the network. ## What You Will Learn In this section, you will go through the following topics: - **Consensus Mechanisms:** Understand what consensus mechanisms are and how they work - **Snowman Consensus:** Learn about the Snowman consensus protocol - **TPS vs TTF:** Understand the difference between transactions per second (TPS) and time to finality (TTF) # Consensus Mechanisms (/academy/avalanche-l1/avalanche-fundamentals/02-avalanche-consensus-intro/02-consensus-mechanisms) --- title: Consensus Mechanisms description: Learn how blockchains arrive at consensus using different mechanisms. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Consensus plays a crucial role in [blockchain networks](/guides/what-is-a-blockchain) by resolving conflicts and ensuring that all validators agree on the current state of the distributed ledger. The main objective of a consensus mechanism is to create a single version of truth that is universally accepted by network participants. Validators can reach a consensus by following a set of steps called a consensus protocol. This way, they collectively decide on the state of the system and all state changes. Different consensus mechanisms have different approaches. All aim to ensure that validators reach a majority agreement on network state. ## Ordering through Consensus Consensus is needed to agree on the order of state changes in a blockchain. This allows the validators to decide between two conflicting states. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/1-aWL9i9BFicUEF7ntMjBFuUejfadAUS.png) ## Double Spending Attack A Double Spending Attack is when a user attempts to spend more crypto than they own by creating multiple transactions that reference the same funds. Let's look at an Example: Alice owns 5 AVAX ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/2-2fU6eqsUiCq76Sy04VEfvD5rLo3eVc.png) Now Alice issues two transactions at the same time to different validators: 1. Send 5 AVAX to Bob 2. Send 5 AVAX to Charly It is important that there is only a limited notion of time in blockchain systems. Even if Alice does not issue these transactions at the exact same time, validators cannot identify which was issued first. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/3-8GHqtWHuf44Nilsugj6yC9sC0BFsZh.png) Each of the transaction in itself is valid. Alice owns 5 AVAX and should be able to send them to whomever she wants. However, she should only be able to send the amount she actually owns. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/4-GeSctGjJKsmXFWvwMpZmYpDcGquZcg.png) Therefore, validators have to collectively come to consensus on which of the two conflicting transactions will be included in the blockchain first, and therefore be accepted by all validators as the next state. To illustrate, we will color the validators preferring Charlie yellow and the validators preferring Bob blue. # Snowman Consensus (/academy/avalanche-l1/avalanche-fundamentals/02-avalanche-consensus-intro/03-snowman-consensus) --- title: Snowman Consensus description: Learn about the Avalanche Snowman consensus protocol. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Protocols in the Avalanche family operate through repeated sub-sampled voting. When a validator is determining whether a block should be accepted, it asks a small, random subset of validators on their preferences. Based on the responses the validator gets, it might change its own preference. Let's visualize this with an example. You are a validator in a set of validators that is performing the Avalanche Consensus protocol to agree on whether to send the funds to Charlie (yellow) or to Bob (blue). It is important to understand that none of the validators really cares whether it is going to be yellow or blue, as long as all correctly operating validators decide for the same outcome at the end of the process. The starting preference is chosen randomly by each validator. ## Changing Preference You start by sampling the current preference of five other nodes and they reply: 2 prefer yellow (Charlie) and 3 prefer blue (Bob). ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/5-f6WnJHv0VAKRNOSd8gFHbO2Mar4mWl.png) Avalanche consensus dictates that a validator changes its preference if an α-majority of the sampled validators agree on another option, and it goes along with this popular choice. Let's set the alpha value to 3 in our example, meaning that we change our preference when 3 out of 5 sampled nodes have another preference. Since 3 out of 5 have replied with blue (Bob) we are changing our own preference to Bob. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/6-TscORkJchAtrT2rG8rG5CPFPilUR1b.png) From now on you will respond with blue, when another validators queries you for your current preference. ## Consecutive Successes Avalanche Consensus does not run for a fixed number of rounds, but until a decision threshold is hit. This means the validator keeps sampling until their preference is confirmed for beta (β) consecutive rounds. Now you query another five validators for their preference. Again, three of the five reply with the preference blue (Bob). Since your current preference is confirmed, you increment a counter for consecutive successes by one. You repeat this sampling process until their preference is confirmed for 8 consecutive rounds. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/7-KYmL0QWax9tmW7uT8bdqu9R5sNwgSs.png) ## Parameters of Avalanche Consensus In our example we used fixed values for how many nodes are sampled, how many are needed to change our preference and how many consecutive rounds of successes we require to finalize our decision. These consensus parameters are formalized in the table below and can be chosen by every node individually to meet their needs. |Symbol|Name|Range|Explanation| |---|---|---|---| |n |Number of Participants|1 to ∞|How many participants take part in the system?| |k |Sample Size|1 to n|How many of these participants get asked every round of sub-sampling?| |α |Quorum Size|1 to k|How many of the asked participants have to have the same preference for me to change my preference?| |β |Decision Threshold|>= 1|How many times does the quorum have to confirm my preference until I finalize my decision?| With these parameters we can illustrate the consensus algorithm as pseudo code: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/8-XzHBlGu1fdnPniQecAkpFfC5JTegVD.png) ## Finalization In the common case when a transaction has no conflicts, finalization happens very quickly. When conflicts exist, honest validators quickly cluster around conflicting transactions, entering a positive feedback loop until all correct validators prefer that transaction. This leads to the acceptance of non-conflicting transactions and the rejection of conflicting transactions. Avalanche Consensus guarantees (with high probability based on system parameters) that if any honest validator accepts a transaction, all honest validators will come to the same conclusion. # Throughput vs. Time to Finality (/academy/avalanche-l1/avalanche-fundamentals/02-avalanche-consensus-intro/04-tps-vs-ttf) --- title: Throughput vs. Time to Finality description: Learn how metrics like throughput and time to finality are different. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- To measure blockchain performance we can use two metrics: - Throughput: How many transaction are finalized per second measured in transactions per second (TPS) - Time to Finality: How long does it take a transaction to go from being submitted to validators to being unchangeable. These metrics are very different. Blockchain builders aspire to high throughput and very short time to finality. ## Highway Analogy Let's take an analogy of a highway. Each car represents a transaction and they all travel the same speed. When you click *send* in your wallet, you're like a car entering the highway. When the transaction is *finalized* and unchangeable, it's like the car reaching its destination. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/9-JT3ORUzCZAILlQJjQaY2nJvFZHEVRL.png) Throughput could be likened to the number of lanes, determining how many cars can pass on the highway in a given amount of time. The more lanes you have, the more cars can pass through, thus increasing the throughput. In a blockchain network, increasing the block size (analogous to adding more lanes) can increase throughput. Now, imagine you're driving to a specific destination (finality). The time it takes you to get from your starting point (initiation of the transaction) to your destination (confirmation of the transaction) is analogous to the "time to finality." As soon as a car reaches finality, it cannot return or abort the travel. Our goal is to build highways with many lanes (high throughput) but that bring us to our destination as fast as possible (short time to finality). ### Throughput and Finality of Popular Blockchain Networks |Network|Throughput|Time to Finality| |---|---|---| |Bitcoin|7 TPS|60 min| |Ethereum|30 TPS|6.4 min| |Avalanche / Avalanche L1|2500 TPS|~0.8 seconds| **TPS** → **Transactions per Second** Returning to the highway analogy, these networks would look like this: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/10-M9xrguxF39gS0MkB03LHE1DcNDGsIV.png) # Multi-Chain Architecture (/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/01-multi-chain-architecture) --- title: Multi-Chain Architecture description: Learn about the Multi-Chain Architecture. updated: 2024-05-31 authors: [ashucoder9] icon: Book --- Multi-chain systems are a significant innovation, which provide greater scalability, customizability, and independence. At the core of multi-chain systems is the ability to run multiple blockchains simultaneously. Each blockchain is optimized for specialized use cases, thereby boosting the network's overall performance. ## What You Will Learn In this section, you will go through the following topics: - **Avalanche L1s:** Understand what Avalanche L1s are and how they work - **Setup Core Wallet:** Learn how to setup the Core wallet browser extension - **Use Dexalot:** Use your first Avalanche L1 # Avalanche L1s (/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/02-L1) --- title: Avalanche L1s description: Learn about the Multi-Chain Architecture. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- > An Avalanche L1 is an independent blockchain with its own set of rules regarding validator membership, tokenomics, and the execution layer. It has it's own validator set that comes to consensus and validates the blockchain. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/l1s.png) These specialized L1 blockchains can be tailored to meet specific needs for a variety of applications, including DeFi, NFTs, gaming, and enterprise solutions. - They specify their own execution logic. - They determine their own fee structures. - They maintain their own state. - They facilitate their own networking. - They provide their own security. This independence ensures that L1s don't have to share execution threads, storage, or networking with other L1s networks or the Primary Network. ## Avalanche Network The Avalanche network is a network of independent interoperable L1s. They can communicate with each other and assets can be transferred between them, while they maintain interoperability. As a result, the Avalanche network can: - Scale up easily - Achieve higher overall transactions per second (TPS) - Offer lower transaction costs By leveraging L1s, Avalanche creates a flexible and scalable ecosystem that can adapt to a wide range of use cases and requirements. # Features & Benefits of Avalanche L1s (/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/03-benefits) --- title: Features & Benefits of Avalanche L1s description: Learn about various benefits of using Avalanche L1s, such as Scalability, Independence, Customizability, Privacy, and Interoperability. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## Scalability of the Avalanche Network Every blockchain has a limited capacity for computation and data storage. Therefore, the transactions it can process in a given time frame and the state it can store are limited. In order to scale horizontally and to offer more blockspace, we can just add more blockchains to the Avalanche Network. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/11-KyUqqw9iwrH60hF4wnYPOWRTBOGRhT.png) We can use the analogy of a highway to visualize this concept. A highway can only handle a certain number of cars at a time. If too many cars try to enter the highway at once, traffic congestion will occur and they have to wait to enter the highway. The idea is similiar to building many highways in parallel and create additional space for cars to drive on. This allows for more transactions to be processed in parallel, and therefore increases the overall throughput of the network. The beauty of this approach is its simplicity. It doesn't require any new untested innovations. The challenge, rather, is optimizing how Avalanche L1s interoperate and making switching from one Avalanche L1 to the other easy. ## Independence From Other Avalanche L1s Separating the ecosystem into different chains creates independence from another. If congestion builds on one chain due to high network activity (e.g. an NFT drop, high volatility in token prices, new game launched), other chains are unaffected. One chain's congestion or increasing fees won't impact other chains. Going back to our highway analogy, we can think of the scaling through multiple chains as building many short highways in parallel. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/12-B730ft1bJPU6lxd3G4B7rpdExaNuC6.png) Coming back to our highway analogy, we can imagine a scenario where congestion builds up on one highway. The other highways are not directly affected. Some cars may choose to take a different highway and the traffic bottleneck may decrease. In single-chain systems, like Ethereum, congestion and rising fees affect everyone, including parties that have nothing to do with the cause of the activity increase. While the unified, or monolithic, blockchain design does offer certain advantages (like unified liquidity, fewer bridges, and potential for enhanced user experience via co-location of dApps), it also introduces notable drawbacks. Validators must have powerful, costly machines to support a diverse array of applications, increasing centralization due to high operational costs of running a node. Lack of modularity can halt all on-chain activities during network disruptions, potentially causing significant financial losses. Additionally, each new dApp competes for the same block space as all others, leading to overcrowding. ## Customizability The creator of an Avalanche L1 can customize it to meet their needs. This can happen in numerous ways, including introducing a custom own gas token, using a different Virtual Machine (e.g. a WASM or Move VM), or limiting access to the Avalanche L1. This is very hard to do in a single-chain system because it would require a majority of users to agree. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/13-VegJrRJXmD83CRfgpx2wTbzLkszjBB.png) In our highway analogy, let's think of some travelers having a very unique requirement: They would like to travel by boat instead of a car. While technically possible to build a water lane on a single highway system, this would be challenging. However, when these custom requirements are met in a Avalanche L1, it's easy to do. The ability to choose or create custom Virtual Machines (VMs) offers unprecedented flexibility. Avalanche L1s allow for developers to have: - Multiple VM Support: Unlike single-VM networks like Bitcoin or Ethereum, Avalanche L1s can host multiple blockchain instances with different VMs. - Ease of Use: Leverage existing VMs like the Subnet-EVM or create entirely new custom VMs using our SDKs to suit your specific needs. - Network Effects: This flexible architecture creates network effects both within individual blockchains and across different Avalanche L1s and blockchains. ## Enhanced Privacy, Compliance and Access Control While public blockchains offer transparency, many business scenarios require controlled visibility of transaction data. Developers building an Avalanche L1 can optionally enable: - Selective Transparency: Private blockchains allow you to limit transaction visibility to authorized participants. - Data Protection: Implement transaction encryption to safeguard sensitive information. - Granular Access: Control which data is visible to which participants, supporting "need-to-know" access models. ## Interoperability Avalanche L1s come with native interoperability and can leverage: - Cross-Chain Communication: Facilitate seamless interaction between different custom blockchains in the Avalanche network leverage Avalanche Interchain Messaging. - Asset Bridges: Create efficient bridges for asset transfers between your custom blockchain and other networks such as the Avalanche Interchain Token Transfer. # Avalanche9000 Upgrade (/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/03a-etna-upgrade) --- title: Avalanche9000 Upgrade description: Learn about how the Avalanche9000 upgrade changes the network architecture. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- import EtnaUpgradeMotivation from '@/content/common/multi-chain-architecture/etna-upgrade-motivation.mdx'; # Avalanche L1s vs Layer 2 (/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/04-custom-blockchains-vs-layer-2) --- title: Avalanche L1s vs Layer 2 description: Comparing different blockchain scaling approaches. updated: 2024-06-28 authors: [usmaneth] icon: BookOpen --- Layer 2 blockchain solutions, such as rollups, are another innovation in the blockchain landscape. Layer 2s aim to enhance the scalability and performance of the Ethereum network. Rollups essentially perform computations off-chain and submit the resultant state changes to the base layer, thereby reducing the computational load on the main Ethereum chain. While both Avalanche Custom Blockchains and Layer 2 rollups strive to improve blockchain scalability and performance, they use different methods and each have their unique advantages and trade-offs. ## Decentralization and Security Avalanche Custom Blockchains are part of the base layer itself. Each blockchain in Avalanche maintains its security, hence a compromise in one blockchain doesn't necessarily impact the others. On the other hand, rollups delegate security to the Ethereum mainnet. As long as the mainnet remains secure, so does the Layer 2 solution, given the rollup performs properly. However, a security breach on the mainnet can potentially affect all Layer 2 solutions. ## Interoperability and Flexibility Avalanche's multi-chain structure offers great interoperability and flexibility, as each custom blockchain network can define its own rules and validate multiple blockchains of different virtual machines. This means Avalanche can cater to a vast array of use cases. Conversely, Layer 2 solutions are primarily designed to augment the Ethereum mainnet and might not offer the same flexibility. ## Performance and Cost Both approaches aim to offer higher transaction throughput and lower fees compared to traditional single-chain systems. Avalanche achieves this through parallel processing across its Avalanche L1s, while rollups offload computation off-chain. However, users of Layer 2 solutions might experience delays when transferring assets back to Layer 1. Furthermore, since Layer 2 systems need to checkpoint their activity to the L1, which effectively sets a price floor and couples the prices of the L1 gas token to the L2 gas token. In Avalanche, the gas tokens of an Avalanche L1 are completely independent from AVAX. # Set Up Core Wallet (/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/05-setup-core) --- title: Set Up Core Wallet description: Learn about how to setup your own Core Wallet. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Step, Steps } from 'fumadocs-ui/components/steps'; import InstallCoreExtension from "@/content/common/core-wallet/install.mdx" import CreateWallet from "@/content/common/core-wallet/create-wallet.mdx" import TestnetMode from "@/content/common/core-wallet/testnet-mode.mdx" import Faucet from "@/content/common/core-wallet/faucet.mdx" To grasp the concept of the Avalanche L1s better we will interact with a chain. To do that, we will use Core. Core is an all-in-one command center built for multi-chain systems. It supports Avalanche, Bitcoin, Ethereum, and all EVM-compatible blockchains. Core has a browser extension, a web wallet, and a mobile app. It is optimized for multiple chains and makes navigating between them easy. ### Install Core Extension ### Create a Wallet ### Switch to Testnet ### Get Testnet Tokens ## Managing MetaMask & Core Extensions If had Metamask already installed, you could face some problems. We recommend temporarily disabling Metamask for the time being. Open the extensions page in browser. You can also type `chrome://extensions` in your address bar to open it. Scroll through your list of installed extensions, or use the search bar at the top of the page, to find the Metamask extension. Once you find Metamask, you will see a toggle switch next to it. Click on this switch to disable the extension. You can reenable it any time. When the switch is in the off position, Metamask is disabled, and you should be all set. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/14-7yGvfoSga3HSD9JF2bokKPBFd3OvdV.png) # Use Dexalot L1 (/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/06-use-dexalot) --- title: Use Dexalot L1 description: Get first hand experience with an Avalanche L1. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Step, Steps } from 'fumadocs-ui/components/steps'; In this activity, we'll interact with our first Avalanche L1. The Dexalot Avalanche L1 aims to replicate the user experience of a centralized exchange (CEX) with greater decentralization and transparency. By using a Avalanche L1, Dexalot can offer cheaper transaction fees than other traditional decentralized exchanges while keeping the transparency that centralized exchanges lack. ### Display the Dexalot Avalanche L1 in Core Make sure your Core Wallet is set to Testnet mode, so we do not use real funds. Open your Core Wallet Browser Extension and click "Show all Networks." To display Dexalot to our home screen, click the star icon next to its name. ### Bridge Tokens to Dexalot Head over to the [Dexalot Testnet](https://app.dexalot-test.com/trade), connect your wallet (top right), and click on the `Deposit` button next to the pink icon with your connected wallet. You will have to authenticate your wallet for the first time you interact with Dexalot. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/Dexalot1.png) Once you clicked the `Deposit` button, select to deposit into the Dexalot L1. Enter 1 AVAX and click Deposit. Confirm the transaction in your wallet. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/Dexalot2.png) A few moments later, your balances should be updated and you should be able to see that on the `Dashboard` tab: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/Dexalot3.png) ### Swap Tokens on Dexalot L1 Now head to the `Trade` tab and sell 0.5 AVAX for USDC. You can select the trading pair AVAX/USDC on the top right. The website will prompt your wallet to switch networks to the Dexalot L1. Confirm the switch and then sign the transaction. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/Dexalot3.png) Now, head back to the `Dashboard` tab and check how the balances updated. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/Dexalot4.png) ### Withdraw to Fuji C-Chain From the `Dashboard` tab, withdraw your AVAX and USDC back to the Fuji C-Chain by clicking on the "Withdraw" gray button on the left: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/Dexalot5.png) Make sure the network is set to Fuji C-Chain and enter the amount you want to withdraw. Click on the Withdraw button and confirm the transaction in your wallet. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/Dexalot6.png) ## What just happened? On the surface, it might feel like a regular decentralized swap. However, there is a substantial difference. We performed our operation not on the C-Chain, but bridged our tokens to a Avalanche L1, performed the operations there, and bridged our tokens back to the C-Chain. The Avalanche L1 (Dexalot) we used was highly specialized for our use case and very cheap. We actually got all the gas token ALOT we needed airdropped, just for bridging assets over. The only fees we had to pay were for bridging. Usually, we would not have done this for a single operation, so these costs do not occur for a single trade, but only for depositing and withdrawing from the portfolio. # Creating an L1 (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/01-creating-an-l1) --- title: Creating an L1 description: Learn how to create an L1 blockchain in the Avalanche network using the Builder Tooling. updated: 2024-05-31 authors: [martineckardt] icon: Book --- Now that we've gone over what Avalanche L1s are and how Interoperability works, you are probably eager to test out the functionality of Avalanche L1s by creating one yourself! In this section, you will learn how to create an L1 blockchain using the the Avalanche Builder Tooling. For production deployments you can leverage a [Blockchain-as-a-Service Provider](/integrations#Blockchain%20as%20a%20Service) to oursurce the setup and maintenance of the L1 infrastructure. ## Video You can watch a video how an L1 is created: ## What You Will Learn In this section, you will go through the following steps: ### Claim Testnet AVAX Tokens In the first step we will show you how to set up the Core wallet chrome extension. After, we will show how to claim some testnet AVAX tokens from the faucet. Finally, you will learn how to bridge the tokens from the C-Chain to the P-Chain. ### Create the P-Chain records for the a Subnet and blockchain Here we will show you how to create the P-Chain records for the Subnet and the blockchain. You will issue a `CreateSubnetTx` transaction on the P-Chain to create a Subnet record. Then you will add a belockchain to the Subnet by issuing a `CreateChainTx` transaction on the P-Chain. ### Set up a node to track the Subnet In this step you will learn how to set up a node to track the Subnet. We will leverage Docker to set up a node on a cloud server. When the node is up and running, you will connect your wallet to it. ### Convert to an L1 To finish the process, you will convert the Subnet to an L1. This is done by issuing a `ConvertSubnetToL1Tx` transaction on the P-Chain. This will turn your node into an actual validator for the L1. ### Test the L1 In the final step, you will deploy a ERC-20 token on the L1 you had launched. # Create Builder Account (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/01a-create-builder-account) --- title: Create Builder Account description: Create a Builder Account for easier access. updated: 2025-03-13 authors: [martineckardt] icon: CircleUserRound --- Before you start creating your first L1, we recommend you to create an Avalanche Builder Account. This will allow you to easily access many features of Builder Hub. You can complete the course without a Builder Account, but it will be much more convenient to have one. ### Benefits of a Builder Account - **Faucet**: With a Builder Account, you can claim testnet AVAX on the C-Chain and P-Chain right from the Builder Console without the need of a coupon code or holding a mainnet balance. - **Free Managed Testnet Infrastructure**: With a Builder Account, we will provide free managed testnet nodes and ICM relayers for your L1. This infrastructure is not suitable for production use, but it is great for testing and development. # Install Core Wallet (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/02-connect-core) --- title: Install Core Wallet description: Learn how to install the Core wallet browser extension and create a wallet. updated: 2025-03-13 authors: [owenwahlgren] icon: Wallet --- In this section you will learn how to set up Core wallet and get some testnet AVAX on the P-Chain so you can launch your first L1. ### Download Core Wallet If you don't have Core Wallet installed already, download the Core Extension for Chrome: [Core Wallet Extension](https://chromewebstore.google.com/detail/core-crypto-wallet-nft-ex/agoakfejjabomempkjlepdflaleeobhb) Core is the only wallet that supports issuing P-Chain transactions, so it is not possible to use other wallets such as MetaMask or Rabby. ### Create a Wallet in Core Create a new wallet in Core by clicking the `Continue with Google` or by manually creating a new Wallet. Congratulations! You have successfully set up your Core wallet. # Claim Testnet Tokens (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/02a-claim-testnet-tokens) --- title: Claim Testnet Tokens description: Learn how to connect Core wallet and claim testnet AVAX. updated: 2025-03-13 authors: [owenwahlgren] icon: Coins --- Connect your Core wallet below to claim testnet AVAX on the C-Chain and P-Chain. With a Builder Hub account, **tokens are automatically sent to your wallet** - no manual claims or coupon codes needed! **Automated Faucet:** Once you connect your wallet with a Builder Hub account, testnet tokens are automatically sent to your wallet when needed. You can also manually request tokens using the buttons above. ## Alternative Faucet If you don't want to create a [Builder Hub Account](https://build.avax.network/login), you can use the external [Avalanche Faucet](https://core.app/tools/testnet-faucet/?subnet=c&token=c) with coupon code `avalanche-academy` or `avalanche-academy25` to get 2 AVAX on the C-Chain. After you have claimed some AVAX on the C-Chain, you can bridge it to the P-Chain using this tool: # Network Architecture (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/03-network-architecture) --- title: Network Architecture description: Learn about the network architecture of Avalanche and the role of the P-Chain. updated: 2025-03-13 authors: [owenwahlgren] icon: BookOpen --- To create an L1 you need to follow these steps: 1. Create a Subnet record on the P-Chain with the `CreateSubnet` transaction 2. Add one or more chains to the Subnet with the `CreateChain` transaction 3. Convert the Subnet to an L1 and add the initial validators with the `ConvertSubnetToL1` transaction Let's dive into what that means: ## Validators in a Multi-Chain Network Validators are nodes of a blockchain that secure the network by validating the transactions. Each L1 in the Avalanche network has it's own set of validators that is running the `AvalancheGo` client. L1 Creation ## Platform Chain The Platform Chain is the backbone for the native interoperability of the Avalanche network. It is a registry of all validators in the Avalanche network. This includes the validators of the Primary Network (including the C- and P-Chain), as well as all L1s and legacy Subnet validators. For each validator the P-Chain stores the following information: - A unique node ID identifying the validator - A BLS Public Key - The Stake Weight - The Balance of the Validator for the continous interoperability fee following graphic shows a simplified data model of the P-Chain: P-Chain Architecture Builders can create new L1 blockchains in the Avalanche Network by issuing transactions on the P-Chain. The P-Chain runs a special-purpose virtual machine called the [platformvm](https://github.com/ava-labs/avalanchego/tree/master/vms/platformvm). It is not EVM-based and therefore to interact with it you need a compatible wallet like Core wallet. Creating new records for L1 blockchains on the P-Chain is done by issuing transactions like the `CreateSubnetTx`. The P-Chain is secured by the Primary Network validators. L1 validators are syncing the P-Chain, meaning they always have the latest view of the validator set of all blockchains in the Avalanche network, but are not participating in the consensus of the P-Chain. ## Subnets When the Avalanche network was created the architecture included the concepts of Subnets. Subnets are blockchains that were validated by a *subset of the Primary Network validators*. Each Primary Network validator can be a member of multiple subnets, and each Subnet can have multiple validators. Since Primary network validators have to fullfil the Primary Network staking requirements of `2,000 AVAX`, this was also necessary for every validator that was validating a Subnet. There was no option to opt-out of validating the Primary Network. Primary Network
Architecture While the concept of Subnets is still supported in the Avalanche network, it is recommended to launch new blockchains as L1s to take advantage of the benefits outlined earlier. Since the naming of Subnets is deep enshrined in the Avalanche network, you will still see it sometimes in the code or the transaction names. # Create a Blockchain (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/05-create-blockchain) --- title: Create a Blockchain description: Learn how to configure a blockchain and create a record for it on the P-Chain by issuing a CreateChainTx transaction using the Builder Tooling. updated: 2025-03-13 authors: [owenwahlgren] icon: SquareMousePointer --- Now that you have Core wallet set up and some AVAX on the P-Chain you can create a Subnet. You will do this by issuing a `CreateSubnetTx` transaction. This will create a Subnet that is uniquely identified by the transaction hash of the `CreateSubnetTx` transaction. The `CreateSubnetTx` transaction only has a single parameter: The owner of the Subnet. The owner can add blockchains to the Subnet and convert it to an L1. With the conversion to an L1 the owner will loose it's privileges. Therefore, the owner is only relevant during the creation time and does not have to be secured by a mulit-sig if a immediate conversion to an L1 is planned. We will just use your P-Chain address as the owner. Then you will issue the `CreateChainTx` on the P-Chain to create the blockchain record. The `CreateChainTx` transaction as the following parameters: - `name`: The name of the chain - `subnetID`: The ID of the Subnet you want to add the chain to - `vmID`: The ID of the Virtual Machine that will be used to run the chain. - `genesisData`: The genesis configuration of the chain The Genesis Builder tool allows us to configure many aspects of the blockchain like permissioning, it's tokenonomics and the transaction fee mechanism. Feel free to browse through the different configuration options, but for now don't change any of the defaults and just click the `View Genesis JSON` button. Then click `Create Chain` This will create the P-Chain record for your blockchain and associate it with the Subnet created in the previous step. The blockchain will be uniquely identified by the transaction hash of the `CreateChainTx` transaction. Congratulations! You have successfully created a blockchain record on the P-Chain. The blockchain does not have any validator nodes yet, so we can't connect our wallet to it or issue any transactions just yet. You will learn how to do that in the next section. # Set up Validator Nodes (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/06-run-a-node) --- title: Set up Validator Nodes description: Learn how to deploy validator nodes for your L1 using managed infrastructure or self-hosted Docker deployments. updated: 2025-03-19 authors: [owenwahlgren] icon: Terminal --- import AvalancheGoDocker from "@/components/toolbox/console/layer-1/AvalancheGoDockerL1"; import ToolboxMdxWrapper from "@/components/toolbox/academy/wrapper/ToolboxMdxWrapper.tsx"; Now that the P-Chain records are set up, you can start syncing the node with your Subnet. ## Run Your L1 Node Use our free managed testnet infrastructure to instantly deploy a node for your L1, no need for Docker or Cloud Provider account set up with credits. Enjoy the benefits of: - Instant deployment with one click - Automatic configuration for your Subnet - Free for testnet development - Monitor and manage through the [Builder Console](/console/testnet-infra/nodes) Managed nodes automatically shut down after 3 days. For production or extended testing, see the self-hosted option below. ## Optional Alternative: Self-Hosted Infrastructure The free managed testnet nodes are a great option for playing around with Avalanche L1s, but are not intended for running on production environments. They are shut down automatically after 3 days. If you want to test out your production environment, running beyond 3 days, or anything more complex you should run nodes on your own infrastructure using Docker: # Convert a Subnet to an L1 (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/07-convert-subnet-l1) --- title: Convert a Subnet to an L1 description: Learn how to convert a Subnet to an L1 on the P-Chain by issuing a ConvertSubnetToL1Tx transaction using the Builder Tooling. updated: 2025-03-13 authors: [owenwahlgren] icon: SquareMousePointer --- The node you have just launched is tracking the Subnet but is not yet a validator. In fact, since the Subnet does not have any validators yet it cannot process any transactions. In this step we will do two things at once: 1. Convert the Subnet to an L1 2. Add your node as a validator Converting a Subnet to an L1 requires the Subnet owner issuing a `ConvertSubnetToL1Tx` transaction on the P-Chain. This transaction establishes a new validator set for your blockchain and transforms it from a Subnet into a sovereign L1 chain. The `ConvertSubnetToL1Tx` transaction is a one-time transaction that can only be executed once by the Subnet owner(s). After the conversion the subnet owner looses all privileges and the L1 is controlled by a validator manager contract, that manages the validator set. You will learn more about this in the next chapter. The `ConvertSubnetToL1Tx` has the following parameters: - **Subnet ID** - The unique identifier of your Subnet - **Validator Manager Blockchain ID** - The ID of the blockchain where the Validator Manager contract will be deployed. This blockchain can belong to the L1, be the C-Chain or belong to any other L1 in the Avalanche network - **Validator Manager Address** - The address of the contract on that blockchain - **Validators** - The initial set of validators for the L1 The **Validator Manager Address** is initially set to an OpenZeppelin [TransparentUpgradeableProxy](https://docs.openzeppelin.com/contracts/4.x/api/proxy) contract pre-deployed in the `genesis.json`. After conversion, you'll deploy the actual `ValidatorManager` implementation contract and update the proxy to point to it. ## Conversion Tool Use the following tool to convert your Subnet to an L1: # Test your L1 (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/08-test-l1) --- title: Test your L1 description: Test your freshly created L1 by deploying an ERC-20 using the Builder Tooling. updated: 2025-03-13 authors: [martineckardt] icon: SquareMousePointer --- Congratulations! You have successfully created your own L1. Now, let's deploy an ERC-20 token on your L1 and test it. You have just launched your own blockchain on Avalanche and already deployed your first token on it. In this following chapters you will learn more on how you can tailor your L1 to your applications needs. # Remove Node (/academy/avalanche-l1/avalanche-fundamentals/04-creating-an-l1/09-remove-node) --- title: Remove Node description: Learn how to stop and remove a node running on Docker. updated: 2025-03-19 authors: [martineckardt] icon: Terminal --- Now that you have tested your L1, you can stop and remove the node. This is useful if you want to free up resources or if you want to start a new node with different parameters. ### Stop Node To stop the node, you can use the following command: ```bash docker stop avago ``` The node credentials and the blockchain state is persisted in the `~/.avalanchego` directory. When you restart the node with `docker start avago` it will pick up where it left off. ### Remove Node ```bash docker rm avago ``` This will not remove the state and credentials of the node. To remove these you need to delete the `~/.avalanchego` directory. # Introduction (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/01-introduction) --- title: Introduction description: Learn about interoperability in the Avalanche ecosystem and its importance in multichain systems updated: 2024-08-26 authors: [martineckardt, nicolasarnedo] icon: Book --- Now that we know about [Avalanche's Multichain Architecture](/academy/avalanche-l1/avalanche-fundamentals/03-multi-chain-architecture-intro/01-multi-chain-architecture) it is important we understand how we achieve **native interoperability** between all of these chains. ## What is Interoperability Interoperability refers to the ability of different blockchain networks to communicate, share data, and interact with each other seamlessly. This capability allows assets, information, and functionalities to move between separate blockchain ecosystems without the need for intermediaries. These interactions can take many forms, including: - Asset Bridging (Tokens, NFTs, etc.) - DAO Voting across Chains - Cross-Chain Liquidity Pools - Decentralized Data Feeds - Cross-Chain Smart Contract Calls ## Why Interoperability Matters Without interoperability, blockchains face significant challenges: - **Lack of Liquidity**: New blockchains struggle to attract sufficient liquidity for tokens and financial instruments, limiting their viability and user adoption. - **Limited Developer Adoption**: Developers are hesitant to build on new blockchains without access to existing tools, communities, and infrastructure from established networks. - **Restricted User Access**: Users face barriers entering new blockchains due to lack of direct on-ramp services and limited asset availability. ## How Avalanche Achieves Interoperability Avalanche enables native cross-chain communication through a layered approach: ### Avalanche Warp Messaging (AWM) The foundational protocol that enables L1s to exchange authenticated messages. AWM leverages BLS multi-signatures where validators sign outgoing messages, and these signatures are aggregated for efficient verification on the destination chain. ### Interchain Messaging Contracts (ICM) Smart contracts that provide a developer-friendly interface on top of AWM. They handle message encoding/decoding, relayer incentives, and cross-chain dApp communication patterns. ### Interchain Token Transfer (ICTT) Built on Interchain Messaging contracts, ICTT enables asset transfers between L1s by locking tokens on the source chain and minting representations on the destination chain. ## What You'll Learn In this section, we'll explore: 1. **Interoperability Fundamentals** - Understanding the core concepts and use cases 2. **Securing Cross-Chain Communication** - Cryptographic foundations including BLS signatures 3. **Avalanche's Interoperability Solutions** - Deep dive into AWM and ICTT protocols 4. **Practical Implementation** - How these technologies work together in real applications Let's begin by understanding the fundamental concepts that make secure cross-chain communication possible. # Source, Message and Destination (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/02-source-message-destination) --- title: Source, Message and Destination description: Learn about interoperability and it's importance in multichain systems. updated: 2024-05-31 authors: [martineckardt] icon: BookOpen --- Interoperability is achieved by enabling blockchains to pass messages to one another. Each message originates from a source chain and is sent to one or more destination chains. These messages encode arbitrary data that can attest some event on the source chains, such as the deposit of an asset or the result of a vote.
![](/common-images/teleporter/source.png) # Source - Origin of communication - Sender calls contract
![](/common-images/teleporter/message.png) # Message - Contains source, destination, and encoded data - Signature guarantees authenticity
![](/common-images/teleporter/destination.png) # Destination - Submission of message as transaction - Verifies signatures
## Source Chain The source chain in a cross-blockchain communication system refers to the original blockchain where data, assets, or information originate before being communicated to another blockchain. It acts as the starting point for initiating cross-chain interactions. When a user intends to communicate with another blockchain, they utilize protocols or smart contracts to initiate the communication process from the source chain. ## Message The message is the data structure that will be sent to to the destination chain. It contains some metadata, the encoded message, and a signature. The signature attests the authenticity of the message, meaning that whatever the message claims has actually happened on the source chain. ## Destination Chain Conversely, the destination chain is the recipient blockchain where the communicated data, assets, or instructions will be received and processed. Validators, nodes, or protocols associated with the cross-blockchain communication system on the destination chain receive and authenticate the information relayed from the source chain. Once validated, the destination chain processes the data or executes the specified instructions. # ICM, ICM Contracts & ICTT (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/03-icm-icmContracts-and-ictt) --- title: ICM, ICM Contracts & ICTT description: Learn how Avalanche implements secure interoperability with ICM, ICM Contracts (Teleporter), and ICTT updated: 2025-07-21 authors: [nicolasarnedo] icon: BookOpen --- We can look at Avalanche's Native Interoperability with a layered approach, each step guaranteeing that our message is securely sent across systems. In order to understand the fundamentals of cross-chain messaging, here are 5 concepts that we will be covering in this chapter that make it possible: 1. **Secure Signatures** - The foundation that ensures messages can be trusted 2. **ICM (Interchain Messaging)** - Native blockchain feature that enables cross-chain communication 3. **ICM Contracts** - Developer-friendly tools that make building cross-chain apps easier 4. **ICTT (Interchain Token Transfer)** - Ready-to-use solution for moving tokens between chains *(not always used)* 5. **Relayers** - service that helps deliver messages between chains by collecting signatures and submitting transactions ![Interchain Messaging Layers](/common-images/avalanche-fundamentals/InterchainMessagingLayers+Relayer.png) Knowing the components is the first piece of the puzzle, grasping how they interact with each other is the next step. In the next image we can see ## Interchain Messaging (ICM) ICM is the foundation of cross-chain communication on Avalanche. It's built directly into every Avalanche blockchain, making it a native feature rather than an add-on. ### What ICM Does Think of ICM as a built-in postal system for blockchains: - **Creates Messages**: Smart contracts can create messages to send to other blockchains - **Signs Messages**: Validators sign these messages to prove they're authentic - **Verifies Messages**: Destination blockchains can verify that messages are genuine ### The Warp Precompile At the heart of ICM is the "warp precompile" - a special smart contract that comes pre-installed on every Avalanche blockchain. Unlike regular smart contracts that developers deploy, this one is built into the blockchain software itself (and is written in go unlike most smart contracts that are in solidity). ### Simple Message Flow ![Interchain Messaging Flow](/common-images/avalanche-fundamentals/InterchainMessagingExampleFlow.png) 1. A smart contract creates a message using the warp precompile 2. The blockchain emits an event saying "I have a message to send" 3. Validators sign this message to prove it's legitimate 4. A relayer picks up the message and signatures 5. The relayer delivers everything to the destination blockchain 6. The destination blockchain verifies the signatures and accepts the message ## ICM Contracts (Teleporter) While ICM provides the basic messaging capability, developers need an easier way to use it. That's where ICM Contracts come in - specifically a contract called "Teleporter". ### What Teleporter Does Teleporter is like a user-friendly interface for ICM: - **Simple Functions**: Instead of complex operations, developers just call `sendCrossChainMessage()` - **Message Management**: Automatically handles encoding, tracking, and delivering messages - **Fee Handling**: Manages payments to relayers for delivering messages - **Security**: Prevents messages from being delivered twice or replayed ### Why Developers Use Teleporter - It's deployed at the same address on every blockchain - It handles all the complex parts of cross-chain messaging - It provides a standard way for contracts to communicate across chains - It makes building cross-chain applications much simpler ## Relayers Relayers are the delivery service of the cross-chain messaging system. They're responsible for physically moving messages from one blockchain to another. ### What Relayers Do 1. **Monitor Blockchains**: Watch for new cross-chain messages 2. **Collect Signatures**: Gather validator signatures that prove a message is valid 3. **Submit Transactions**: Create and submit the transaction on the destination blockchain 4. **Pay Gas Fees**: Cover the transaction costs on the destination chain (and get reimbursed) ### Key Points About Relayers - Anyone can run a relayer - it's permissionless - Relayers need wallets with tokens on destination chains to pay for gas - They can be configured to handle specific routes or all messages - They're incentivized through fee mechanisms built into Teleporter ## ICTT: An Example Cross-Chain Application ICTT (Interchain Token Transfer) is not a core component of cross-chain messaging - it's an application built on top of ICM, Teleporter, and relayers. Think of it as one of many possible cross-chain applications. ### What ICTT Does ICTT is a pre-built solution specifically for moving tokens between blockchains: - **Token Home**: Manages the original tokens on their native blockchain - **Token Remote**: Creates wrapped versions of tokens on other blockchains - **Transfer Logic**: Handles locking, minting, burning, and releasing tokens ### Why ICTT is Special While anyone could build their own token bridge using ICM and Teleporter, ICTT provides: - A tested, secure implementation - Standard contracts that work the same way everywhere - Support for both native tokens and ERC-20 tokens - Advanced features like "send and call" for composability Think of ICTT like a pre-built e-commerce platform - you could build your own, but using the existing solution saves time and reduces risk. ## Summary Avalanche's cross-chain messaging system works like a well-coordinated postal service: **The Core Components:** - **ICM**: The built-in messaging system in every blockchain - **ICM Contracts (Teleporter)**: The easy-to-use interface for developers - **Relayers**: The delivery service that moves messages between chains **Why It Works So Well:** - **Trust**: Messages are secured by the same validators that run the blockchains - **Simplicity**: Developers can send messages with just a function call - **Flexibility**: Anyone can build cross-chain applications (like ICTT for tokens) - **Speed**: Messages are delivered in seconds, not minutes or hours This design means developers can focus on building their applications instead of worrying about the complex infrastructure of cross-chain communication. ## What Powers This System? Now that we understand how messages travel from one blockchain to another, you might wonder: what makes this system secure? How do we know a message really came from the source blockchain and hasn't been tampered with? The answer lies in cryptographic signatures - the digital equivalent of a tamper-proof seal. In the next sections, we'll explore: - How validators create these digital signatures - Why multiple signatures make the system secure - How signatures can be efficiently combined and verified Understanding these concepts will complete your knowledge of how Avalanche achieves secure, native interoperability. For hands-on practice with cross-chain messaging, check out the [Academy Interchain Messaging](/academy/interchain-messaging) course. # Signature Schemes (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/04-signature-schemes) --- title: Signature Schemes description: Learn how Signature Schemes work and enable secure cross-chain communication updated: 2024-05-31 authors: [martineckardt] icon: BookOpen --- import SignatureSchemes from "@/content/common/cryptography/signature-schemes.mdx" import defaultMdxComponents from "fumadocs-ui/mdx"; # Use a Signature Scheme (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/05-signature-demo) --- title: Use a Signature Scheme description: Hands-on demonstration of BLS signature schemes updated: 2024-05-31 authors: [martineckardt] icon: Terminal --- import SignatureSchemesDemo from "@/content/common/cryptography/signature-schemes-demo.mdx" # Multi-Signature Schemes (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/06-multi-signatures) --- title: Multi-Signature Schemes description: Learn how multiple signatures provide Byzantine fault tolerance updated: 2024-05-31 authors: [martineckardt] icon: BookOpen --- import MultiSignatureSchemes from "@/content/common/cryptography/multi-signature-schemes.mdx" # Use Multi-Signature Schemes (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/07-multi-signature-demo) --- title: Use Multi-Signature Schemes description: Hands-on demonstration of multi-signature schemes with BLS updated: 2024-05-31 authors: [martineckardt] icon: Terminal --- import SignatureSchemesDemo from "@/content/common/cryptography/multi-signature-schemes-demo.mdx" # BLS Signature Aggregation (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/08-signature-aggregation) --- title: BLS Signature Aggregation description: Learn how signature aggregation enables efficient multi-party verification updated: 2024-05-31 authors: [martineckardt] icon: BookOpen --- import SignatureKeyAggregation from "@/content/common/cryptography/signature-key-aggregation.mdx" import defaultMdxComponents from "fumadocs-ui/mdx"; # Use Cases (/academy/avalanche-l1/avalanche-fundamentals/05-interoperability/09-use-cases) --- title: Use Cases description: Learn about the practical applications of interoperability on Avalanche updated: 2024-08-26 authors: [owenwahlgren] icon: BookOpen --- Interoperability enables various use cases that enhance user experience and expand the Avalanche ecosystem's capabilities. Let's explore the key applications: ## 1. Cross-Chain Token Transfers Tokens can be seamlessly transferred across different L1 blockchains without centralized exchanges: - **Seamless Transfers**: Move tokens (like USDC) between chains with minimal fees and fast transaction times - **Liquidity Access**: Leverage liquidity from various networks for DeFi activities, trading, or payments - **Decentralization**: Maintain control over assets during transfers without third-party intermediaries ## 2. Decentralized Data Feeds Smart contracts can access reliable price feeds and other data from specialized oracle chains: - **Chainlink Integration**: Utilize real-time price data to power DeFi applications - **Trustless Access**: Obtain data directly from decentralized oracles - **Cross-Chain Applications**: Build financial applications that rely on consistent data across networks ## 3. Cross-Chain Token Swaps Direct token swaps between different blockchain networks without centralized exchanges: - **Decentralized Exchange**: Swap assets in a trustless environment - **Multi-Chain Access**: Access tokens from various ecosystems - **Lower Costs**: Avoid high fees of centralized platforms ## 4. Cross-Chain NFTs NFTs can be transferred and utilized across different blockchain networks: - **Broad Exposure**: NFTs can be showcased and traded across multiple chains - **Enhanced Utility**: Use NFTs in gaming, art, and virtual worlds across platforms - **Ownership Preservation**: Maintain authenticity and provenance across chains ## 5. Interoperable DeFi Protocols DeFi protocols can interact across chains for enhanced functionality: - **Cross-Chain Yield Farming**: Maximize returns across multiple chains - **Cross-Chain Collateralization**: Use assets from any chain as collateral - **Composability**: Build innovative financial products leveraging multiple networks ## 6. Cross-Chain Governance Decentralized governance that spans multiple blockchains: - **Unified Governance**: Govern multi-chain protocols from a single platform - **Decentralized Voting**: Enable participation from token holders on different chains - **Enhanced Participation**: More diverse and representative decision-making ## Real-World Example: Gaming Consider a gaming ecosystem where: - Game assets (NFTs) are minted on one L1 optimized for low fees - The game logic runs on another L1 optimized for high throughput - Rewards are distributed on a third L1 with established DeFi infrastructure Interoperability makes this seamless for users who can play, trade, and earn across all chains without friction. # Independent Tokenomics (/academy/avalanche-l1/avalanche-fundamentals/07-independent-tokenomics/01-independent-tokenomics) --- title: Independent Tokenomics description: Quickly recap our past learnings about Avalanche Custom Blockchains. updated: 2024-06-28 authors: [usmaneth] icon: Book --- Avalanche Custom Blockchains offer multiple ways to implement independent tokenomics. This gives developers more control and can enable new business models that would not be economically feasible on single-chain systems. The customizations include: - **Native Token:** Every Avalanche L1 has its own native token used for paying transaction fees. - **Transaction Fees:** We can configure how the transaction fees should be calculated. - **Initial Native Token Allocation:** We can specify how the inital token supply is distributed. - **Native Token Minting Rights:** We can specify if and who can mint more native tokens. - **Staking Token:** If our Avalanche L1 allows public and permissionless validation, we can define our logic how a node can become a validator. You will learn about all these topics and get hands-on experience how you can configure the tokenomics of your own custom blockchain. # Staking Token (/academy/avalanche-l1/avalanche-fundamentals/07-independent-tokenomics/11-staking-token) --- title: Staking Token description: Learn about staking tokens in blockchain networks. updated: 2024-06-28 authors: [usmaneth] icon: BookOpen --- In many networks, such as Ethereum, the same token is used for staking and paying for gas. In the Avalanche network staking tokens and gas tokens can be separated, since they fullfil different purposes within the blockchain ecosystem. Staking tokens are used for securing public permissionless Avalanche L1s through a proof-of-stake (PoS) consensus mechanism. Holders of staking tokens can run validators and participate in the consensus process by staking a certain amount of tokens as collateral. Validators propose and validate new blocks of your L1 blockchain. Staking tokens play a crucial role in maintaining network security and incentivizing their participation. Not all Avalanche L1s are public and permissionless. Many enterprises choose a Proof-of-Authority setup, where the validators are selected by the enterprise. These blockchains do not have a staking token. You will learn about setting up Proof-of-Stake in the L1 Validator Management course. # Introduction to VM Customization (/academy/avalanche-l1/avalanche-fundamentals/07b-vm-customization/00-vm-customization) --- title: Introduction to VM Customization description: Learn about customizing Virtual Machines. updated: 2024-05-31 authors: [ashucoder9] icon: Book --- For some use cases, it may be necessary to use a customized VM too. This is the case if an application cannot be built on a regular EVM on the C-Chain, or if it would result in gas costs too high to be economical for its users or creators. Depending on a builder's needs, Avalanche allows for different VM customizations: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/32-BJ1Orj9c9p0VxpSTUJlWH1jPvo8nlD.png) Customizing the EVM on the Ethereum network is difficult, requiring a wide consensus that the proposed change is mutually beneficial for all network participants. This can make customizations for unique use cases challenging, if not impossible. Additionally, Ethereum doesn't give users the option to add different chains. In Avalanche, every Avalanche L1 has autonomy over their Virtual Machines. Their creators can customize VMs to fit their unique requirements. This is one of the biggest advantages of multi-chain systems. In this section, we will see the three way to customize Virtual Machines at a high level. In later courses, we will dive deeper and learn how to actually customize each. # VM Configuration (/academy/avalanche-l1/avalanche-fundamentals/07b-vm-customization/01-configuration) --- title: VM Configuration description: Learn about customizing Virtual Machines. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- When building a VM, it is possible to define certain parameters that can change the behavior of the VM. In our soda dispenser analogy these may be the products and prices offered by the dispenser. We might want to have two dispenser blockchains that offer different products and prices. If the VM is built in a way that it has parameters for the products and prices, it can be easily reused for different use items. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/33-AibP9P6YHVSun8IyXBAkKAJMeXVLqp.png) This is a massive advantage over one-chain-fits-all systems, where the parameters have to be a compromise between all network participants. In Avalanche it is possible to have different blockchain with the same VM, but different parameters. Using VM Configuration we can easily create EVM-chains for different use cases such as trading a cheap gaming NFT or valuable Real estate on chain. These blockchains may differ in fees (low fees for cheap NFTs, high fees for valuable goods) and security levels (low security for cheap NFTs, high security for valuable goods). Examples of the configurable parameters of the subnetEVM include: **trxAllowList:** Define a whitelist of accounts that restrict which account's transactions are accepted by the VM. **contractDeployerAllowlist:** Defined a whitelist of accounts that restricts which account can deploy contracts on the blockchain Using these parameters we can adapt the VM to our requirements without writing a single line of code. This is by far the easiest, but also least flexible way to customize a VM to one's requirements. ```json { "config": { "chainId": 99999, "homesteadBlock": 0, "eip150Block": 0, "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "istanbulBlock": 0, "muirGlacierBlock": 0, "subnetEVMTimestamp": 0, "feeConfig": { "gasLimit": 20000000, "minBaseFee": 1000000000, "targetGas": 100000000, "baseFeeChangeDenominator": 48, "minBlockGasCost": 0, "maxBlockGasCost": 10000000, "targetBlockRate": 2, "blockGasCostStep": 500000 }, "contractDeployerAllowListConfig": { "blockTimestamp": 0, "adminAddresses": [ "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" ] } }, "alloc": { "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { "balance": "0x52B7D2DCC80CD2E4000000" }, "0x0Fa8EA536Be85F32724D57A37758761B86416123": { "balance": "0x52B7D2DCC80CD2E4000000" } }, "nonce": "0x0", "timestamp": "0x0", "extraData": "0x00", "gasLimit": "0x1312D00", "difficulty": "0x0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } ``` You can find more examples of Genesis files [here](https://github.com/ava-labs/subnet-evm/tree/master/tests/precompile/genesis). # VM Modification (/academy/avalanche-l1/avalanche-fundamentals/07b-vm-customization/02-modification) --- title: VM Modification description: Learn about modifying Virtual Machines. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- The next more powerful way to customize a VM is VM Modification. To meet our requirements, we can modify and extend our existing VM. In our soda dispenser analogy, we might want to customize our soda dispenser to accept card payments. Our current soda dispenser machine simply does not have this feature. Instead of reinventing the wheel and building a new dispenser with the new feature from the ground up, we simply extend the existing VM using a plugin. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/34-C4A2siQ7sVC3E0NAYfJ60Kr6AJDVQA.png) In Avalanche, creating modified VMs is straightforward. The subnetEVM for instance can be customized through the development of plugins as well as precompiles. # VM Creation (/academy/avalanche-l1/avalanche-fundamentals/07b-vm-customization/03-creation) --- title: VM Creation description: Learn about creating Virtual Machines. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- For some use cases it might be necessary to create entirely new VMs. In our analogy this would be the case if we require completely different features, let's say we want to build a car VM. There is simply no way we can configure or extend a soda dispenser to turn it into a car. Instead we have to build a new VM from ground up. These newly created VMs can cater to much more specialized use cases and therefore this enables completely new blockchain applications not possible before. VM Creation is by far the most complex way, but also the most powerful way to customize a VM. It requires a deep understanding of the blockchain space and software development skill. There might be certain basic elements in most VMs, such as the notion of transaction, accounts and authentication. Therefore, Avalanche provides some SDKs, such as the HyperSDK, to make the creation of VMs in languages like Go and Rust easier. When creating a VM, there are two very distinct design patterns, that greatly vary in their intended use case. ## Special Purpose VMs Special Purpose VMs are highly optimized for a specific use case. They only allow a very limited set of operations closely knit to its use case. A popular example for this would be the VM of the Bitcoin blockchain. The operations are much more limited than the ones of the Ethereum Virtual Machines, hence it only allows transactions related to the transfer of BTC. ## General Purpose General Purpose VMs introduce another layer - commonly know as Smart Contracts or Programs - that can be submitted to the VM and enable user of the chain to interact with these. This way it allows its users to introduce their own logic to the VM. The most common General Purpose VM is the Ethereum Virtual Machine (EVM). However, some Avalanche L1s creators might want to create a VM that utilizes another language, such as Move, instead of Solidity as its smart contract language. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/avalanche-fundamentals/35-ZGXXBzP8NzWvLQm5y1giGjRTfATKwO.png) One of the EVM's key features is its capability to execution of smart contracts in a deterministic and trustless manner, ensuring that the outcome of their execution is predictable and verifiable by all network participants. Developers can create decentralized applications and deploy them on the Ethereum blockchain. This enables a wide range of use cases including decentralized finance (DeFi), tokenization, supply chain management, and more. The EVM's flexibility, security, and compatibility have made it a fundamental component of the Ethereum ecosystem, powering a vibrant and rapidly evolving ecosystem of decentralized applications and services. # Permissioning (/academy/avalanche-l1/avalanche-fundamentals/08-permissioning-users/01-permissioning) --- title: Permissioning description: Learn about different ways of permissioning your Avalanche L1 blockchain. updated: 2024-06-28 authors: [usmaneth] icon: Book --- Permissioning your Avalanche L1 is an optional feature of having your own L1 blockchain. There may be many reasons why a blockchain creator may want to permission their network including these: - **Privacy and Confidentiality:** In industries where data privacy is crucial, such as finance or healthcare, permissioned blockchains allow only authorized parties to access sensitive information. This ensures that confidential data remains protected from unauthorized access. - **Regulatory Compliance:** Many industries are subject to strict regulatory requirements regarding data handling and security. Permissioned blockchains enable organizations to implement necessary controls to ensure compliance with regulatory standards without compromising the integrity of the blockchain. - **Cost-efficiency:** Restricting the usage of a blockchain to certain use cases may make it more cost efficient. Blockchain operators may be able to avoid transaction fee spikes caused events such as NFT drops by imposing restrictions. These permissions don't necessarily have to be administered by a centralized entity. There could also be a DAO in charge of determining who should be allowed to use the blockchain. In the upcoming lessons, you will learn about the different levels of Permissions and how they can be configured for your Avalanche L1. # Compliance (/academy/avalanche-l1/avalanche-fundamentals/08-permissioning-users/02-compliance) --- title: Compliance description: Learn how you can configure compliance for your Avalanche L1. updated: 2024-06-28 authors: [usmaneth] icon: BookOpen --- For institutions, reputational damage and legal complications could arise if their blockchain systems were inadvertently used for criminal activities. Regulatory bodies might impose penalties on such institutions if they were to fail to create sufficient controls to prevent these activities. Most institutions have nuanced compliance requirements. Therefore, many institutions need flexible blockchains that can meet these needs. ## Interacting with Criminal Actors In permissionless systems, the counterparty on a trade is unknown. If a user performs a swap on a decentralized exchange on a permissionless blockchain, such as Ethereum or the Avalanche C-Chain, the origin of funds that are received are mostly unknown. To solve this problem, each Avalanche L1 can restrict who can interact with the blockchain. Only a whitelist of accounts can issue transactions. One could build a blockchain where only accounts that went through a KYC process are permitted. ## Interacting with Illegal Services An additional risk to consider is that businesses may operate within an environment prone to illicit activities, or they may inadvertently become involved in such actions. In a permissionless system, any individual has the capability to deploy any contract, regardless of its compliance with the legal framework. Avalanche L1s can limit who deploys smart contracts on the blockchain. Consequently, those involved in the creation of contracts can be held accountable. This level of control reassures institutions, ensuring that all protocols they engage with are fully compliant with existing laws. Beyond compliance advantages, this approach helps to maintain a blockchain free from an excess of smart contracts that could otherwise congest the system. # Transaction Allowlist (/academy/avalanche-l1/avalanche-fundamentals/08-permissioning-users/04-tx-allowlist) --- title: Transaction Allowlist description: Learn how to restrict transactions to a specific set of addresses. updated: 2024-06-28 authors: [usmaneth] icon: BookOpen --- import TxAllowList from "@/content/common/evm-precompiles/transaction-allowlist.mdx"; # Activate Transaction Allowlist (/academy/avalanche-l1/avalanche-fundamentals/08-permissioning-users/05-activate-tx-allowlist) --- title: Activate Transaction Allowlist description: Learn how to activate the the transaction allowlist precompile in the genesis. updated: 2024-06-28 authors: [martineckardt] icon: Terminal --- Avalanche L1s do not implement permissioning by default. To enable permissioning, you need to activate the transaction allowlist precompile in the genesis. This allows only approved addresses to issue transactions on your blockchain. # Contract Deployer Allowlist (/academy/avalanche-l1/avalanche-fundamentals/08-permissioning-users/06-contract-deployer-allowlist) --- title: Contract Deployer Allowlist description: Learn how to restrict contract deployment to a specific set of addresses. updated: 2024-06-28 authors: [martineckardt] icon: BookOpen --- import ContractDeployerAllowlist from "@/content/common/evm-precompiles/contract-deployer-allowlist.mdx"; # Activate Contract Deployer Allowlist (/academy/avalanche-l1/avalanche-fundamentals/08-permissioning-users/07-activate-contract-deployer-allowlist) --- title: Activate Contract Deployer Allowlist description: Learn how to activate the contract deployer allowlist precompile in the genesis. updated: 2024-06-28 authors: [martineckardt] icon: Terminal --- Analogous to the transaction allowlist, Avalanche L1s do not implement contract deployer allowlist by default. To enable permissioning, you need to activate the contract deployer allowlist precompile in the genesis. This allows only approved addresses to deploy contracts on your blockchain. # Permissioning Validators (/academy/avalanche-l1/avalanche-fundamentals/09-permissioning-validators/01-permissioning-validators) --- title: Permissioning Validators description: Learn about different ways of permissioning your Avalanche L1 blockchain. updated: 2024-06-28 authors: [usmaneth] icon: Book --- The ability to control a blockchain's validator set has many benefits that can significantly enhance the efficiency, security, and governance of the network. It allows blockchain participants to have greater influence over the consensus process and decision-making, leading to a more robust and adaptable ecosystem. There are two ways to structure the validator set: - **Permissionless Validation:** Anyone can participate in the validation process and be rewarded in tokens to cover their costs for the hardware and running the validator. In Avalanche, we call Avalanche L1s that take this approach Elastic Avalanche L1s. - **Permissioned Validation:** Only whitelisted validators can validate the Avalanche L1. A permissioned Avalanche L1 can be turned into a Elastic Avalanche L1 at any time, but not the other way round. There are many reasons why the creators might want a permissioned validator set for their Avalanche L1. You will learn in detail about the technical options in the L1 Validator Management course. ## Enterprises and Corporations: Large enterprises and corporations often require blockchain solutions for their internal operations or specific industry use cases. A permissioned validator set allows them to control who can participate in the network and validate transactions. This is particularly relevant in industries with strict regulatory requirements, where entities need to ensure compliance and data privacy. ## Consortiums and Industry Associations: Consortiums or industry associations comprising multiple organizations with shared interests can benefit from a permissioned validator set. These entities often collaborate on initiatives requiring a distributed ledger, such as supply chain management, healthcare data sharing, or financial transactions. By establishing a permissioned validator set, the consortium can ensure that trusted participants from within the consortium validate the transactions. ## Government Agencies: Government entities may find value in launching a blockchain with a permissioned validator set to manage critical infrastructure, public services, or regulatory processes. They can select validators from trusted institutions or stakeholders to maintain control over the network while ensuring compliance with legal and governance requirements. Examples include land registry systems, voting platforms, or identity management solutions. ## Financial Institutions: Banks, payment processors, and other financial institutions could be interested in a permissioned validator set for blockchain solutions related to payments, remittances, or settlement systems. These institutions often require a level of control over the network to maintain regulatory compliance, prevent money laundering, and ensure adherence to Anti-Money Laundering (AML) regulations. # Private Blockchains (/academy/avalanche-l1/avalanche-fundamentals/09-permissioning-validators/02-private-blockchains) --- title: Private Blockchains description: Learn about different ways of permissioning your Avalanche L1 blockchain. updated: 2024-06-28 authors: [usmaneth] icon: BookOpen --- On a public blockchain, every transaction and change to the blockchain's state is visible to all. Anyone with internet access can see the transaction data. Block Explorers are one way to conveniently access the data on a chain. Although the identities of participants are often pseudonymous, the transaction data itself is open for anyone to see. However, there are many instances, especially in business or enterprise settings, where such transparency isn't desirable. A company might be conducting a high-value transaction that it doesn't want competitors or others in the market to see. There might also be legal or regulatory reasons for needing to keep transaction data private. In a private, permissioned blockchain, the visibility of transactions is limited to a select group of individuals or entities. This way, sensitive data isn't exposed. Only those who have been granted the necessary permissions can view the transaction data, and these permissions can be tightly controlled and monitored by network administrators. Further, transactions in a private blockchain can be encrypted, adding an extra layer of security. Even within the network, details about specific transactions can be hidden from certain participants, based on their level of permission. This allows for a much higher degree of confidentiality, as specific data can be revealed only on a need-to-know basis. In summary, privacy in the context of private, permissioned blockchains isn't just about restricting who can join. It's about having granular control over who can see what data, and when. It's about providing a secure, confidential environment where sensitive transactions can occur without the risk of exposure to unintended parties. This has made private, permissioned blockchains an attractive option for businesses and organizations dealing with sensitive data, high-value transactions, or strict confidentiality requirements. ## Private Blockchains The data of Avalanche L1s, by default, are publicly available. This means that every node can sync and listen to ongoing transactions/blocks in these blockchains, even if they're not validating the network. Nodes may do this for indexing purposes or just having access to the current state of the blockchain without relying on third-parties. Avalanche L1 validators can choose not to publish data from their blockchains. If a node sets `validatorOnly` to true, the node exchanges messages only with the blockchain's validators. Other peers won't be able to learn contents of this blockchain from their nodes. # Intain Markets Case Study (/academy/avalanche-l1/avalanche-fundamentals/09-permissioning-validators/03-case-study-intain-markets) --- title: Intain Markets Case Study description: Learn about Intain's permissioned Avalanche L1. updated: 2024-06-28 authors: [usmaneth] icon: Microscope --- Intain operates a structured finance platform. It has **IntainMARKETS**, a marketplace for tokenized asset-backed securities built as an Avalanche L1. The digital marketplace automates and integrates functions of key stakeholders in structured finance, including issuer, verification agent, underwriter, rating agency, servicer, trustee, and investor. Rather than replacing trust intermediaries, it integrates them onto a single platform to enable digital issuance and investment with a complete on-chain workflow. All parties work together in a transparent but private environment. ## Permissioned Validators and Tokenomics The **IntainMARKETS L1** also uses other ways to adhere to regulatory requirements. It has U.S-hosted infrastructure, which allows data to reside domestically, while validators chosen by network participants must also be verified U.S. entities and individuals. The Avalanche L1 economics are not dependent on any public token, and transaction costs are independent from those of the Avalanche C-Chain and other Avalanche L1s. Further Readings: [Intain Launch Announcement](https://medium.com/avalancheavax/intain-launches-avalanche-subnet-to-usher-in-new-era-for-multi-trillion-dollar-securitized-877c7cc1031f) # Origin of the EVM (/academy/avalanche-l1/customizing-evm/02-intro-to-evm/01-origin-of-evm) --- title: Origin of the EVM description: Learn about the origin of the Ethereum Virtual Machine. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: BookOpen --- The **Ethereum Virtual Machine (EVM)** is a fundamental component of Ethereum’s infrastructure, responsible for executing smart contracts across the network. The origins of the EVM can be traced back to the creation of the Ethereum blockchain itself, proposed in a whitepaper by **Vitalik Buterin** in late 2013. Buterin and the founding Ethereum team envisioned a platform where developers could build **decentralized applications (dApps)** on a blockchain. To accomplish this, they needed a way to execute arbitrary, complex computations in a secure and decentralized manner. This is how the concept of the EVM was born. The EVM, an integral part of the Ethereum ecosystem, operates as a **quasi-Turing complete machine**. This means it can run nearly any algorithm, given enough resources. It's isolated from the main network, providing a sandboxed environment for smart contract execution. Following the publication of the whitepaper, the Ethereum project moved into development. The first live version of the Ethereum network, including the EVM, launched on **July 30, 2015**. The Ethereum blockchain was designed with the EVM at the protocol level, enabling users to create and execute smart contracts. These smart contracts are essentially programs that autonomously execute the terms of an agreement. They are written in high-level languages like **Solidity** or **Vyper**, which are then compiled down to EVM bytecode for execution on the EVM. Since its creation, the EVM has become a crucial component of the Ethereum blockchain and has set a standard for other blockchain platforms that have adopted similar models for executing smart contracts. The advent of the EVM has undoubtedly revolutionized the blockchain world by introducing the concept of **programmable blockchains**. # Accounts, Keys, and Addresses (/academy/avalanche-l1/customizing-evm/02-intro-to-evm/02-accounts-keys-address) --- title: Accounts, Keys, and Addresses description: Learn about Accounts, Keys, and Addresses in EVM. updated: 2024-09-27 authors: [ashucoder9, owenwahlgrne] icon: BookOpen --- ## Accounts In the EVM, there are two types of accounts: 1. **Externally Owned Accounts (EOAs)**: These accounts are controlled by private keys and do not have associated code. They can send transactions by creating and signing them with their private keys. If you're an end user of Ethereum, you're likely using an EOA. 2. **Contract Accounts**: These accounts have associated code (smart contracts). Contract accounts can't initiate transactions on their own; instead, they only perform actions when instructed by an EOA. This could be as simple as a token transfer or a function call within a smart contract. ## Public and Private Keys In Ethereum, as in many blockchain platforms, the **Elliptic Curve Digital Signature Algorithm (ECDSA)** is used to generate a pair of keys: a public key and a private key. - The **private key** is a 32-byte number generated randomly. - The **public key** is derived from the private key using elliptic curve cryptography. It is 64 bytes long, made up of two concatenated 32-byte coordinates. ## Addresses An EVM address is derived from the public key through the following steps: 1. **Keccak-256 Hashing**: The public key is first passed through the Keccak-256 hashing function (the version of SHA-3 used by Ethereum). Keccak-256 outputs a 64-byte string, for example: `025ad33e2479a53b02dc0be66eb0ce272fc0f4358c57a8f0a442410c3d831a` 2. **Rightmost 20 bytes**: The resulting string is truncated to keep only the last 20 bytes. In hexadecimal, this means retaining the rightmost 40 hex digits (since two hex digits represent one byte), like so: `e66eb0ce272fc0f4358c57a8f0a442410c3d831a` 3. **Adding '0x'**: Finally, "0x" is prefixed to the address for a total of 42 characters. The "0x" indicates that the following characters represent a hexadecimal number, a common convention in Ethereum: `0xe66eb0ce272fc0f4358c57a8f0a442410c3d831a` This process ensures that each Ethereum address uniquely corresponds to a public key. Since the address is a truncated hash of the public key, it's impossible to reverse-engineer the public key from the address. # Transactions and Blocks (/academy/avalanche-l1/customizing-evm/02-intro-to-evm/03-transactons-and-blocks) --- title: Transactions and Blocks description: Learn about another fundamental aspect of the Ethereum ecosystem - Transactions and Blocks. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: BookOpen --- ## Transactions In the EVM, a transaction is the smallest unit of work that can be included in a block. It represents a **state transition**, modifying account data and transferring Ether across the network. Transactions can either transfer Ether or invoke smart contracts. Each transaction includes the following components: - **Nonce**: A unique value set by the sender to ensure the transaction is processed only once, preventing replay attacks. - **Gas Price**: The amount the sender is willing to pay per unit of gas, typically measured in `nanoAvax` (1 `nAvax` = 1/(10^9) Avax). It consists of three parts: - **Base Gas Fee**: The minimum `nAvax` required per unit of gas. - **Maximum Priority Gas Fee**: Additional `nAvax` the sender is willing to pay on top of the base fee. - **Maximum Total Gas Fee**: The maximum `nAvax` the sender is willing to spend per unit of gas. If the sum of the base gas fee and the priority gas fee exceeds this amount, the gas price paid will be capped at the maximum total gas fee. - **Gas Limit**: The maximum amount of gas units the sender is willing to pay for executing the transaction. If this limit is reached, the transaction fails, preventing indefinite execution. - **To**: The recipient's Ethereum address or the target smart contract. - **V, R, S**: Cryptographic data used to create the sender's signature. ## Blocks A block in the EVM is a bundle of validated transactions, grouped together and linked to the previous block. Each block contains: - **Block Number**: The index of the block, representing its position in a zero-indexed linked list. - **Timestamp**: The time the block was mined. - **Transactions Root**: The Merkle root of all transactions within the block. - **Receipts Root**: The Merkle root of all transaction receipts. - **State Root**: A hash of the entire Ethereum state after executing all transactions in the block. - **Parent Hash**: The hash of the previous (parent) block. - **Gas Limit**: The maximum gas all transactions in the block can consume. - **Gas Used**: The total gas consumed by all transactions in the block. - **Extra Data**: An optional field for including arbitrary data up to 32 bytes. - **Validator**: The address of the node that proposed the block. # Different Versions of EVM (/academy/avalanche-l1/customizing-evm/02-intro-to-evm/05-different-evm-versions) --- title: Different Versions of EVM description: Learn about the different versions of the Ethereum Virtual Machine in the Avalanche ecosystem. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: BookOpen --- ## Geth Geth (or [go-ethereum](https://github.com/ethereum/go-ethereum)) is the official implementation of an Ethereum client, written in the Go programming language. It is one of the original and most widely used Ethereum clients today. Geth handles transactions, deploys and executes smart contracts, and contains the Ethereum Virtual Machine. ## Coreth [Coreth](https://github.com/ava-labs/avalanchego/tree/master/graft/coreth) is a fork of Geth maintained by Ava Labs. It implements the EVM for the C-Chain and has been adapted to work with Avalanche Consensus. ## Subnet-EVM [Subnet-EVM](https://github.com/ava-labs/subnet-evm) is a fork of Coreth designed to facilitate launching customized EVM-based blockchains on an Avalanche L1. It differs from Coreth in the following ways: - **Configurable Fees and Gas Limits**: Customizable fees and gas limits can be set in the genesis file. - **Unified Subnet-EVM Hardfork**: All Avalanche hardforks are merged into a single "Subnet-EVM" hardfork. - **Atomic Transactions and Shared Memory Removed**: Support for atomic transactions and shared memory has been removed. - **Multicoin Contracts and State Removed**: Multicoin contracts and state tracking are no longer supported. ## Precompile-EVM Precompile-EVM allows precompiles to be registered with Subnet-EVM without needing to fork the Subnet-EVM codebase. This simplifies common customizations to Subnet-EVM, making them more accessible and easier to maintain. It also streamlines updates for Subnet-EVM. # Set Up Development Environment (/academy/avalanche-l1/customizing-evm/03-development-env-setup/00-intro) --- title: Set Up Development Environment description: Start by setting up your development environment. updated: 2024-05-31 authors: [ashucoder9] icon: Book --- It's time to set up the development environment necessary for customizing the EVM! While the primary focus of this course is teaching you about precompiles, it's also crucial to configure a development environment that ensures a smooth and efficient workflow. In this section, we will cover the following: - Creating a GitHub repository for our customized EVM - Setting up a default test account in your Core Wallet - Exploring different types of development setups - Choosing the setup for developing precompiles Let's get started! 🚀 # Create Codespaces (/academy/avalanche-l1/customizing-evm/03-development-env-setup/02-create-codespaces) --- title: Create Codespaces description: Learn how to create GitHub Codespaces. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- ## Open Precompile-EVM Repository in a Github Codespace Open a Codespace on the [Precompile-EVM](https://github.com/ava-labs/precompile-evm/tree/avalanche-academy-start) Repository on the `avalanche-academy-start` branch by clicking the button below: [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=avalanche-academy-start&repo=605560683&machine=standardLinux32gb) Next a window opens and the Codespace is built. This may take a few minutes. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/customizing-evm/11-Z5CM89rf9qvVIENBpZF2wlKt3bALSq.png) When the building is completed, you can access the Codespace through the browser IDE. ## Closing Codespaces The codespace time out after 30 minutes of inactivity by default. So if you are not too worried about the 30 hour/month limit, just close the window or the tab. Alternatively, you can open the command prompt (Cmd + Shift + P) and issue the command: **Stop Current Codespace**. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/customizing-evm/12-rESxJrPr97KL08SWhP30BMEjHN7UAS.png) ## Reopen Codespaces You can see your Codespaces on https://github.com/codespaces/. There you can stop them, reopen them and delete them. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/customizing-evm/13-BHHk6FjT3nu1ATITEUdeJhezVCpd6G.png) # Codespace in VS Code (/academy/avalanche-l1/customizing-evm/03-development-env-setup/03-codespace-in-vscode) --- title: Codespace in VS Code description: Learn how to setup GitHub Codespaces. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- ## Switch from Browser to VS Code You can switch any time from the browser IDE to Visual Studio Code: ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/customizing-evm/14-aVKH0qkDNUpTwxRaB1qSFNuSk55Mfu.png) The first time you switch, you will be asked to install the [Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces) and connect VS Code to you GitHub account, if it is not already connceted. # Your Own EVM Blockchain (/academy/avalanche-l1/customizing-evm/04-your-evm-blockchain/00-intro) --- title: Your Own EVM Blockchain description: Learn how to spin up your own EVM blockchain. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: Book --- In this part of the course, we'll explore how to run your own Avalanche L1 with a custom EVM. Running your own EVM allows you to address specific use cases, showcasing one of the key advantages of multi-chain systems over monolithic blockchains. ## Topics We’ll cover the following topics: - **Avalanche CLI**: Learn how to configure and launch an Avalanche L1 using the Avalanche CLI. - **Token Transfer**: Explore how to perform token transfers with Foundry. This hands-on exercise will solidify your knowledge and allow you to observe how customizations impact EVM performance. ## Learning Objective By the end of this section, you’ll have the skills to effectively run your own Avalanche L1 with a custom EVM blockchain, empowering you to start building your blockchain projects! # Avalanche CLI (/academy/avalanche-l1/customizing-evm/04-your-evm-blockchain/01-avalanche-cli) --- title: Avalanche CLI description: Learn about the Avalanche Command-Line Interface tooling. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## What is the Avalanche CLI? The Avalanche CLI is a command-line tool that gives developers comprehensive access to Avalanche's functionalities, making it easier to build and test independent blockchains. Each Avalanche network includes the Primary Network, which consists of the Contract (C), Platform (P), and Exchange (X) chains. It's important to note that "Primary Network" refers to a special Avalanche L1 rather than a distinct, standalone network. Your local network operates independently from both the Mainnet and Fuji Testnet. You can even run an Avalanche L1 offline. Local Avalanche networks support, but are not limited to, the following commands: - **Start and Stop a Network**: Easily start or stop a local network. - **Health Check**: Check the health status of each node in the network. - **Create Blockchains**: Spin up new blockchains with custom parameters. Managing a local network with multiple nodes can be complex, but the Avalanche CLI simplifies the process with user-friendly commands. ## Usage The Precompile-EVM repository comes preloaded with the Avalanche CLI and Foundry, so you don’t need to install additional tools when working within Codespaces. Just use the terminal in your Codespace to run Avalanche CLI commands and start building. # Create Your Blockchain (/academy/avalanche-l1/customizing-evm/04-your-evm-blockchain/02-create-your-blockchain) --- title: Create Your Blockchain description: Learn how to use Avalanche-CLI to spin up your own EVM blockchain. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import CreateDefaultBlockchain from "@/content/common/avalanche-starter-kit/create-default-blockchain.mdx"; import defaultMdxComponents from "fumadocs-ui/mdx"; # Sending Tokens (/academy/avalanche-l1/customizing-evm/04-your-evm-blockchain/03-sending-tokens) --- title: Sending Tokens description: Learn how to send tokens on your EVM blockchain. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- To ensure that the blockchain is up and running, let's perform a simple token transfer to a random address, `0x321f6B73b6dFdE5C73731C39Fd9C89c7788D5EBc`, using Foundry: ```bash cast send --rpc-url myblockchain --private-key $PK 0x321f6B73b6dFdE5C73731C39Fd9C89c7788D5EBc --value 1ether ``` To verify if the transaction was successful, check the balance of the address with the following command: ```bash cast balance --rpc-url myblockchain 0x321f6B73b6dFdE5C73731C39Fd9C89c7788D5EBc ``` ```bash 1000000000000000000 ``` You should see that the balance of the address `0x321f6B73b6dFdE5C73731C39Fd9C89c7788D5EBc` is now 1 (1 * 10^18). Congratulations! You have successfully sent tokens on your EVM blockchain. 🎉 # EVM Configuration (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/00-vm-configuration) --- title: EVM Configuration description: Learn about Virtual Machine configuration. updated: 2024-09-27 authors: [ashucoder9] icon: Book --- In this part of the course, we'll explore how to optimize your EVM through chain configuration, tailoring it to fit specific use cases. Customizing EVM configurations is a key advantage of multi-chain systems. ## Exercise In this section, you won’t need to write any Go code. Instead, we’ll adjust values in the JSON file of the genesis block. ## Topics We will cover the following topics: - **Genesis Block**: The foundation of any blockchain. We’ll review its components and how to customize its properties. - **Fee Configuration**: Learn how to balance validator incentives with user affordability. This is crucial for managing network congestion and discouraging wasteful transactions on public networks. - **Initial Token Allocation**: Understand how to define the initial token distribution in your custom EVM, setting your network up for success. - **Preinstalled Precompiles**: Discover how to configure preinstalled precompiles to leverage features like restricting who can issue transactions or deploy contracts on your chain. Finally, we’ll demonstrate how to run a local EVM with a custom Genesis Block. This exercise will solidify your understanding and allow you to observe the performance impact of your EVM customizations. ## Learning Objective By the end of this section, you'll be able to effectively customize the EVM in Avalanche, unlocking new possibilities for your blockchain projects. Let’s dive into EVM customization and chain configuration together! # Genesis Block (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/01-genesis-block) --- title: Genesis Block description: Learn about the Genesis Block. updated: 2024-09-27 authors: [ashucoder9] icon: BookOpen --- ## Background Each blockchain begins with a genesis state when it is created. For instance, the Ethereum mainnet genesis block included the addresses and balances from the Ethereum pre-sale, marking the initial distribution of ether. For Subnet-EVM and Precompile-EVM, the genesis block contains additional parameters that allow us to configure the behavior of our customized EVM to meet specific requirements. Since each blockchain has its own genesis block, you can create two blockchains with the same VM but different genesis blocks. ## Format Here’s an example of a genesis block: ```json { "config": { "chainId": 43214, "homesteadBlock": 0, "eip150Block": 0, "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "istanbulBlock": 0, "muirGlacierBlock": 0, "subnetEVMTimestamp": 0, "feeConfig": { "gasLimit": 15000000, "minBaseFee": 25000000000, "targetGas": 15000000, "baseFeeChangeDenominator": 36, "minBlockGasCost": 0, "maxBlockGasCost": 1000000, "targetBlockRate": 2, "blockGasCostStep": 200000 }, "allowFeeRecipients": false, "txAllowListConfig": { "blockTimestamp": 0, "adminAddresses": [ "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" ] } }, "alloc": { "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { "balance": "0x295BE96E64066972000000" } }, "nonce": "0x0", "timestamp": "0x0", "extraData": "0x00", "gasLimit": "0xe4e1c0", "difficulty": "0x0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } ``` We will explore the relevant configurable parameters in the upcoming activities. Some parameters (e.g., `eip150Block`, `byzantiumBlock`) are omitted here as they are not relevant for most use cases. This version provides a clean and concise explanation of the genesis block and its structure, keeping the focus on what's essential. # Create Your Genesis File (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/02-create-your-genesis) --- title: Create Your Genesis File description: Learn how to create your own genesis file. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Callout } from 'fumadocs-ui/components/callout'; ## Create File In your Precompile-EVM project, create a file called `evm-configuration-genesis.json` in the directory `tests/precompile/genesis/` and open it. You can use the command below as a shortcut to open the file in VSCode: ```bash code ./tests/precompile/genesis/evm-configuration-genesis.json ``` ## Fill with template Paste the following template in the new file: ```json { "config": { "chainId": , "homesteadBlock": 0, "eip150Block": 0, "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "istanbulBlock": 0, "muirGlacierBlock": 0, "subnetEVMTimestamp": 0, "feeConfig": { "gasLimit": , "minBaseFee": , "targetGas": , "baseFeeChangeDenominator": 36, "minBlockGasCost": , "maxBlockGasCost": , "targetBlockRate": , "blockGasCostStep": }, "allowFeeRecipients": false }, "alloc": { "": { "balance": "" } }, "nonce": "0x0", "timestamp": "0x0", "extraData": "0x00", "gasLimit": , "difficulty": "0x0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } ``` If you do decide to set your own gasLimit, please set all gasLimit keys equal to the same value! If you set the 'gasLimit' keys to different values, you will still be able to deploy a blockchain, but it will halt during initialization! # Setup Your ChainID (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/03-setup-chainid) --- title: Setup Your ChainID description: Learn how to setup ChainID for your own blockchain. updated: 2024-09-27 authors: [ashucoder9] icon: Terminal --- ## What is ChainID? The `ChainID` of an EVM blockchain is a unique identifier that distinguishes Ethereum chains from one another. Introduced in Ethereum Improvement Proposal (EIP) 155, this mechanism prevents transaction replay attacks, which could occur when the same transaction is valid across multiple chains. ## Setting the ChainID To set the `ChainID` for your blockchain, configure it in the `genesis` JSON file at the top level with the value `99999`. If you wish to use a custom `ChainID`, check [chainlist.org](https://chainlist.org) to ensure your proposed `ChainID` is not in use by any other network. Be sure to cross-check with Testnets as well! ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/customizing-evm/28-LNcOo16PhvaDVIb7crVasGVKvs8kR6.png) # Gas Fees and Gas Limit (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/04-gas-fees-and-limit) --- title: Gas Fees and Gas Limit description: Learn about Gas Fees and Gas Limit in the context of the EVM. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## Background In the context of the EVM, gas is a unit that measures the computational effort required to execute specific operations. Each operation performed by a contract or transaction on an EVM chain consumes a certain number of gas units based on its complexity. Operations that require more computational resources cost more gas. The EVM calculates the required gas units automatically, and developers are encouraged to optimize their contract code to reduce gas consumption. The cost of executing a transaction is determined by the gas units consumed and the gas price, calculated as follows: ``` Transaction Cost = Gas Units * Gas Price ``` For EVM Avalanche L1s, gas payment can be configured to better suit the use case of the Avalanche L1. This means that the Avalanche L1 design can decide whether the gas fees are burned, paid to incentivize validators, or used for any other custom behavior. ## Purpose The primary goal of setting and enforcing computational costs via gas is to prevent spam and abuse on the network. By requiring users to pay for each computational step, the network deters malicious actors from launching denial-of-service (DoS) attacks, which involve flooding the network with spurious transactions. Essentially, the gas system serves as a deterrent against such attacks. ## Gas Price and Gas Limit Each transaction specifies the gas price and gas limit: **`Gas Price`:** The gas price is the amount of the Avalanche L1's native token that the sender is willing to spend per unit of gas, typically denoted in `gwei` (1 native token = 1,000,000,000 `gwei`). A well-designed gas mechanism adapts the gas price according to network activity to protect the network from spam. **`Gas Limit`:** The gas limit is the maximum amount of gas the sender is willing to use for the transaction. It was introduced to prevent infinite loops in contract execution. In a Turing-complete language like Solidity (the main programming language in the EVM), it is possible to write a contract with an infinite loop, either accidentally or intentionally. While an infinite loop might be a nuisance in traditional computing, it could cause significant issues in a decentralized blockchain by causing the network to hang as it attempts to process a never-ending transaction. The gas limit prevents this by halting execution once the gas consumed reaches the limit. If a transaction exceeds the gas limit, it fails, but the fee amounting to the gas limit is still paid by the sender. # Gas Fees Configuration (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/05-gas-fee-configuration) --- title: Gas Fees Configuration description: Learn how to configure gas fees in your EVM blockchain. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## Configuration Format The fees are configured in the `chainConfig` in the `feeConfig` field: ```json { "config": { // ... "feeConfig": { // [!code highlight] "gasLimit": 15000000, "minBaseFee": 25000000000, "targetGas": 15000000, "baseFeeChangeDenominator": 36, "minBlockGasCost": 0, "maxBlockGasCost": 1000000, "targetBlockRate": 2, "blockGasCostStep": 200000 }, "allowFeeRecipients": false }, "alloc": { // ... }, // ... "gasLimit": 0xe4e1c0, // ... } ``` ## Gas Configuration Parameters ### `gasLimit` Sets the maximum amount of gas consumed per block. This restriction caps the computational capacity of a single block and thereby limits the maximum gas usage allowed for any single transaction. For reference, the C-Chain value is set to 15,000,000. You might notice that the `gasLimit` field appears twice. This is because Avalanche introduced its own fee configuration under the `feeConfig` key while maintaining compatibility with the standard EVM configuration. Ensure that both fields have the same decimal and hexadecimal equivalent values. ### `targetBlockRate` Specifies the target rate of block production in seconds. For instance, a target of 2 aims to produce a block every 2 seconds. If blocks are produced faster than this rate, it signals that more blocks are being issued to the network than anticipated, leading to an increase in base fees. For C-Chain, this value is set to 2. ### `minBaseFee` Establishes a lower bound on the EIP-1559 base fee for a block. This minimum base fee effectively sets the minimum gas price for any transaction included in that block. ### `targetGas` Indicates the targeted amount of gas (including block gas cost) to be consumed within a rolling 10-second window. The dynamic fee algorithm adjusts the base fee proportionally based on how actual network activity compares to this target. If network activity exceeds the `targetGas`, the base fee is increased accordingly. ### `baseFeeChangeDenominator` Determines how much to adjust the base fee based on the difference between actual and target utilization. A larger denominator results in a slower-changing base fee, while a smaller denominator allows for quicker adjustments. For C-Chain, this value is set to 36, meaning the base fee changes by a factor of 1/36 of the parent block's base fee. ### `minBlockGasCost` Sets the minimum amount of gas charged for the production of a block. In the C-Chain, this value is set to 0. ### `maxBlockGasCost` Specifies the maximum amount of gas charged for the production of a block. ### `blockGasCostStep` Defines how much to increase or decrease the block gas cost based on the time elapsed since the previous block. If a block is produced at the target rate, the block gas cost remains the same as the parent block. If the production rate deviates from the target, the block gas cost is adjusted by the `blockGasCostStep` value for each second faster or slower than the target block rate. # Configure Gas Fees (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/06-configuring-gas-fees) --- title: Configure Gas Fees description: Learn how to configure gas fees in your EVM blockchain. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- ## Benchmarks You can take these numbers below as a benchmark: ```json "feeConfig": { "gasLimit": 15000000, "minBaseFee": 25000000000, "targetGas": 15000000, "baseFeeChangeDenominator": 36, "minBlockGasCost": 0, "maxBlockGasCost": 1000000, "targetBlockRate": 2, "blockGasCostStep": 200000 }, "gasLimit": 0xe4e1c0, ``` ## Choose Your Own Values ### `gasLimit` As previously discussed, the `gasLimit` acts as a restriction on the amount of computation that can be executed in a single block, and hence caps the maximum transaction size, as the entire transaction needs to be processed within the same block. Your choice of `gasLimit` values should depend on the specific use case and computational demands of your application. Here are some considerations: **High Throughput:** If your application requires a high volume of transactions, you might want to allow for more transactions (i.e., more computation) to be packed into a single block. This increases the block capacity and enables handling a higher transaction load. **Heavy Transactions:** Transactions that involve deploying large contracts or executing complex operations tend to consume more gas. If your application involves such operations, ensure that your `gasLimit` accommodates the full execution of these heavy transactions or contract deployments. Be cautious with setting excessively high `gasLimit` values. Larger computational allowances per block can translate to increased hardware requirements for your blockchain. Therefore, align the `gasLimit` with your use case and infrastructure requirements for validators. Avoid setting an unusually large value "just in case." For reference, consider that a typical native token transaction costs around 21,000 gas units, and the C-Chain `gasLimit` is set to 15,000,000. ### Gas Price Parameters The following parameters affect the gas pricing and dynamic adjustments in your network: - **`minBaseFee`**: Sets the minimum base fee per unit of gas, establishing the lower bound for transaction costs in a block. - **`targetGas`**: Defines the target amount of gas to be consumed within a rolling 10-second window. Adjust this based on the expected network activity under normal conditions. - **`baseFeeChangeDenominator`**: Controls how quickly the base fee adjusts in response to fluctuations in gas usage. A smaller denominator allows for faster adjustments. - **`minBlockGasCost`**: Specifies the minimum gas cost for producing a block. - **`maxBlockGasCost`**: Sets the maximum gas cost for producing a block. - **`targetBlockRate`**: Determines the desired block production rate in seconds. Choose this based on your expected block issuance frequency. - **`blockGasCostStep`**: Adjusts the block gas cost based on the deviation from the target block rate. A higher step value increases the block gas cost more rapidly with deviations. These parameters should be selected based on the stability of gas usage in your network. Since gas fees are crucial for protecting against spam and abuse, carefully consider how these parameters will adapt to changes in network activity. The goal is to dynamically adjust transaction costs during irregular activity periods while maintaining stability under normal conditions. # Initial Token Allocation (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/07-initial-token-allocation) --- title: Initial Token Allocation description: Learn about the initial token allocation and configure in your EVM blockchain. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Callout } from 'fumadocs-ui/components/callout'; ## Background `Alloc` defines the initial balances of addresses at the time of chain creation. This field should be modified according to the specific requirements of each chain. If no genesis allocation is provided, you won't be able to interact with your new chain, as all transactions require a fee to be paid from the sender's balance. Without an initial allocation, there will be no funds available to cover transaction fees. ## Format The `alloc` field expects key-value pairs. Keys must be valid addresses, and the balance field in each value can be either a hexadecimal or decimal number representing the initial balance of the address. ```json { "config": { // ... }, "alloc": { // [!code highlight] "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { "balance": "0x295BE96E64066972000000" // 50,000,000 tokens } }, // ... } ``` Keys in the allocation are hex addresses without the canonical 0x prefix. Balances are denominated in `Wei` (10^18 `Wei` = 1 Whole Unit of the native token of the chain) and expressed as hex strings with the canonical 0x prefix. Use [this converter](https://www.rapidtables.com/convert/number/hex-to-decimal.html) for translating between decimal and hex numbers. The default configuration for testing purposes allocates a significant number of tokens to the address `8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC`. The private key for this address (as defined in the `.devcontainer`) is: `56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027`. Never use this address or private key for anything other than testing on a local test network. The private key is publicly known, and any real funds transferred to this address are likely to be stolen. ## Configure Allocate tokens to two addresses: - The well-known test address `8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC`. - Another test address that you have created (avoid using addresses associated with real funds). ```json { "config": { // ... }, "alloc": { "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { // [!code highlight] "balance": "0x295BE96E64066972000000" // 50,000,000 tokens }, "": { // [!code highlight] "balance": "0x295BE96E64066972000000" // 50,000,000 tokens } }, // ... } ``` # Build and Run Custom Genesis EVM (/academy/avalanche-l1/customizing-evm/05-genesis-configuration/08-build-and-run-custom-genesis-blockchain) --- title: Build and Run Custom Genesis EVM description: Learn how to build and run your blockchain with custom genesis file. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- ### Build Your Precompile-EVM There's a simple build script in the Precompile-EVM we can utilize to build. First, make sure you are in the root folder of you Precompile-EVM: ```bash cd $GOPATH/src/github.com/ava-labs/precompile-evm ``` Then run the command to initiate the build script: ```bash ./scripts/build.sh ``` ### Create your blockchain configuration You can run your Precompile-EVM by using the Avalanche CLI. First, create the configuration for your blockchain: ```bash avalanche blockchain create myblockchain \ --custom \ --vm $VM_PATH \ --genesis "./.devcontainer/genesis-example.json" \ --force \ --sovereign=false \ --evm-token "TOK" \ --warp \ --icm ``` ### Launch L1 with you customized EVM ```bash avalanche blockchain deploy myblockchain --local ``` After around 1 minute, the blockchain should have been created, and additional output will appear in the terminal. You'll also see the RPC URL of your blockchain in the terminal. # What are Precompiles? (/academy/avalanche-l1/customizing-evm/06-precompiles/01-what-are-precompiles) --- title: What are Precompiles? description: Learn about Precompiled Smart Contracts in EVM. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Precompiled contracts allow the execution of code written in the low-level programming language Go from the EVM, which is significantly faster and more efficient than Solidity. ## Overview If you're familiar with Python, you might recognize a similar concept where many Python functions and libraries are implemented in C for efficiency. Python developers can import these precompiled modules and call functions as if they were written in Python. The main difference is that the modules execute faster and more efficiently. Precompiles can be called from a Solidity smart contract just like any other contract. The EVM maintains a list of reserved addresses mapped to precompiles. When a smart contract calls a function of a contract at one of these addresses, the EVM executes the precompile written in Go instead of the Solidity contract. For example, if we map the address `0x030...01` to the SHA256 precompile that hashes its input using the SHA256 hash function, we can call the precompile as follows: ```solidity // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ISHA256 { // Computes the SHA256 hash of value function hashWithSHA256(string memory value) external view returns(bytes32 hash); } contract MyContract { ISHA256 mySHA256Precompile = ISHA256(0x0300000000000000000000000000000000000001); function doSomething() public { bytes32 hash = mySHA256Precompile.hashWithSHA256("test"); } } ``` In the code above, we call the precompile using the defined interface for our SHA256 precompile within `MyContract`. Note that there is no implementation of the precompile in Solidity itself. This will only work if the precompile is implemented in Go and registered at the address `0x030...01`. ### PrecompiledContract Interface When implementing a precompile in the Avalanche L1-EVM, the following function of the `StatefulPrecompiledContract` interface must be implemented in Go: ```go // StatefulPrecompiledContract is the interface for executing a precompiled contract type StatefulPrecompiledContract interface { // Run executes the precompiled contract. Run(accessibleState AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) } ``` We will cover the meaning of "stateful" and the first parameter `accessibleState` later. For now, let's focus on the function that specifies the logic of our precompile, which provides access to the following data: - caller: The address of the account that called the precompile. - addr: The address of the precompile being called. - input: All inputs encoded in a byte array. - suppliedGas: The amount of gas supplied for the precompile call. - readOnly: A boolean flag indicating if the interaction is only reading or modifying the state. The precompile implementation must return the following values: - ret: All return values encoded in a byte array. - remainingGas: The amount of gas remaining after execution. - err: If an error occurs, return it. Otherwise, return nil. # Why Precompiles? (/academy/avalanche-l1/customizing-evm/06-precompiles/02-why-precompiles) --- title: Why Precompiles? description: Learn about why you should utilize Precompiles in your smart contracts. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Adding precompiles to the EVM offers several significant advantages, which we will outline in this chapter. ## Performance Optimization Precompiles primarily optimize the performance of specific computations. Introducing a new precompile can greatly reduce the computational resources required for certain tasks, thereby enhancing the performance of smart contracts and decentralized applications (DApps) that rely on these tasks. For instance, the SHA256 hash function (0x02) and the RIPEMD160 hash function (0x03) serve as examples of precompiles that significantly boost performance. Implementing these functions within a smart contract would be computationally expensive and slow, whereas as precompiles, they execute quickly and efficiently. ## Security Incorporating a function as a precompile allows developers to leverage libraries that have been thoroughly reviewed and audited, thus reducing the risk of bugs and vulnerabilities, which enhances overall security. For example, the ModExp (0x05) precompile safely performs modular exponentiation, a complex mathematical operation utilized in various cryptographic functions. ## Access to Go Libraries Precompiles are implemented in Go, allowing access to the rich ecosystem of existing Go libraries. This access eliminates the need for reimplementation, which can be labor-intensive and carries the risk of introducing bugs during the translation from Go to Solidity. Consider the implementation of the SHA256 hash algorithm to understand the complexity involved in reimplementing it in Solidity. ## Gas Efficiency Introducing a new precompile to the EVM can enhance gas efficiency for specific computations, thereby lowering execution costs. This makes it feasible to incorporate more complex operations into smart contracts, expanding their functionality without significantly increasing transaction costs. The identity precompile (0x04), which copies and returns input data, exemplifies this. Though simple, it provides gas efficiency by being faster and cheaper than implementing the same functionality in a standard contract. ## Advanced Features and Functionality By adding new precompiles to the EVM, developers can introduce advanced features and functionalities, such as complex mathematical calculations, advanced cryptographic operations, and new data structures. This can unlock new possibilities for DApps and smart contracts, enabling them to execute tasks that would otherwise be too computationally demanding or technically challenging. Precompiles for elliptic curve operations, such as ecadd (0x06), ecmul (0x07), and ecpairing (0x08), enable advanced cryptographic functionality within EVM smart contracts. These precompiles are crucial for implementing zk-SNARKs, a form of zero-knowledge proof, in Ethereum. ## Interoperability Certain precompiles can enhance the interoperability of the EVM with other blockchains or systems. For instance, precompiles can be utilized to verify proofs from other chains or perform operations compatible with different cryptographic standards. The BLS12-381 elliptic curve operations precompiles (0x0a through 0x13, added in the Istanbul upgrade) improve EVM interoperability by allowing operations that are compatible with the BLS signature scheme, potentially facilitating inter-blockchain communication. # Interact with a Precompile (/academy/avalanche-l1/customizing-evm/06-precompiles/03-interact-wtih-precompile) --- title: Interact with a Precompile description: Learn about why you should utilize Precompiles in your smart contracts. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- So let's get to it and interact with a precompile on the C-Chain of your local network. The SHA256 precompile is already available on the C-Chain. ## Call Precompile from Foundry In this example, we will call the SHA256 precompile to generate hash of the input string. **Precompile Address:** `0x0000000000000000000000000000000000000002` ```bash cast call --rpc-url local-c --private-key $PK 0x0000000000000000000000000000000000000002 "run(string)(bytes32)" "test" ``` You should see a bytes32 hash of the input string `test` as the output. ```bash 0xa770b926e13a31fb823282e9473fd1da9e85afe23690336770c490986ef1b1fc ``` # Overview (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/00-intro) --- title: Overview description: Learn how to create a Hash Function Precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Book --- import {Step, Steps} from 'fumadocs-ui/components/steps'; ## What We're Building In this section, we'll create a precompile for the MD5 hash function. By utilizing the existing Go library for this hash function, we can avoid reimplementing the algorithm in Solidity and take advantage of Go's superior efficiency. A hash function is a special type of function that takes input data of any size (such as a letter, a word, the text of a book, or even all combined texts available on the internet) and converts it into a fixed-size output. This output, known as a hash value, represents the original data. The process is often referred to as "hashing." Hash functions possess several key properties: - **Deterministic**: Given the same input, a hash function will always produce the same output. For instance, hashing the word "apple" a thousand times will yield the same hash value each time. - **Fixed Size**: Regardless of the input data size—whether it's a single character or an entire novel—the hash function always produces an output (the hash value) of a consistent length. - **Fast Computation**: Hash functions are designed to be quick and efficient, returning a hash value with minimal computational resources and time. - **Preimage Resistance**: It's computationally infeasible to retrieve the original input data from the hash value alone. This property is especially crucial in cryptographic hash functions for data security. - **Avalanche Effect**: A small change to the input should cause significant changes in the output, making the new hash value appear uncorrelated with the old one. For example, hashing "apple" and "Apple" should produce completely different hash values. - **Collision Resistance**: It is highly unlikely for two different pieces of input data to yield the same hash value. However, due to the limited length of hash values, collisions are theoretically possible. A good hash function minimizes the occurrence of such collisions. Hash functions are utilized in various applications across computer science, including data retrieval, data integrity verification, password storage, and in blockchain technology for cryptocurrencies like Bitcoin. ## Reference Implementation To aid your understanding of building precompiles, we will compare each step with a reference example: a precompile for the SHA256 hash function. Both precompiles are quite similar, featuring a single function that returns the hashed value. ## Overview of Steps Here's an overview of the steps involved: Create a Solidity interface for the precompile. Generate the ABI. Write the precompile code in Go. Configure and register the precompile. Build and run your customized EVM. Connect Remix to your customized EVM and interact with the precompile. Let's get started! # Create an MD5 Solidity Interface (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/01-create-solidity-interface) --- title: Create an MD5 Solidity Interface description: Learn how to create an MD5 Solidity Interface updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- The first step is defining the interface that will wrap the precompile implementation and that other contracts and users will interact with. In addition to declaring the way users can interact with our MD5 precompile, defining the MD5 interface will also allow us to utilize a generator script. A precompile consists of many files, and generating boilerplate Go files will make implementing the precompile much easier. ## SHA-256 Precompile Interface Before defining the MD5 interface, we will first look at the interface of the very similar SHA-256 precompile. This reference implementation is included in the repository we have created earlier. ```solidity title="contracts/contracts/interfaces/ISHA256.sol" // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ISHA256 { /// Compute the hash of value /// @param value the value to be hashed /// @return hash the hash of the value function hashWithSHA256(string memory value) external view returns(bytes32 hash); } ``` ISHA256 contains a single function `hashWithSHA256`. `hashWithSHA256` takes in a value of type string, which is the value which is to be hashed, and outputs a 32-byte hash. ## Creating the Solidity Interface For The MD5 Precompile Now it's your turn to define a precompile interface! Create the interface for the MD5 hash function. Start by going into the same directory where `ISHA256.sol` lives (`contracts/contracts/interfaces/`) and create a new file named `IMD5.sol`. Note that: → MD5 returns a 16-byte hash instead of a 32-byte hash → Make sure to name all parameters and return values ```solidity title="contracts/contracts/interfaces/IMD5.sol" // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IMD5 { function hashWithMD5(string memory value) external view returns (bytes16 hash); } ``` ## Generate the ABI Now that we have an interface of our precompile, let's create an ABI of our Solidity interface. Open the integrated VS Code terminal (control + \`), and change to the `/contracts` directory. ```bash cd contracts ``` Run the command to compile the solidity interface to the ABI: ```bash npx solc@latest --abi ./contracts/interfaces/IMD5.sol -o ./abis --base-path . --include-path ./node_modules ``` Rename the file: ```bash mv ./abis/contracts_interfaces_IMD5_sol_IMD5.abi ./abis/IMD5.abi ``` Now, you should have a file called `IMD5.abi` in the folder `/contracts/abis` with the following content: ```json [ { "inputs": [ { "internalType": "string", "name": "value", "type": "string" } ], "name": "hashWithMD5", "outputs": [ { "internalType": "bytes16", "name": "hash", "type": "bytes16" } ], "stateMutability": "view", "type": "function" } ] ``` # Generate the Precompile (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/02-generate-the-precompile) --- title: Generate the Precompile description: Learn how to generate your precompile. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: Terminal comments: true --- import { File, Files, Folder } from 'fumadocs-ui/components/files'; In the last section, we created the ABI for our precompile contract. Now, we'll use the precompile generation script provided by the precompile-evm template to generate a boilerplate code in go for the precompile implementation that will be wrapped in the solidity interface we created in the previous step. ## Running the Generation Script To start, go to the root directory of your precompile-evm project: ```bash cd .. ``` Now generate the files necessary for the precompile. ```bash ./scripts/generate_precompile.sh --abi ./contracts/abis/IMD5.abi --type Md5 --pkg md5 --out ./md5 ``` Now you should have a new directory in your root directory called `md5`: ### `contract.go` For the rest of this chapter, we'll work with the `md5/contract.go` file. If you generated the Go files related to your precompile, `contract.go` should look like the code below. Do not be intimidated if much of this code does not make sense to you. We'll cover the different parts and add some code to implement the logic of our MD5 precompile. ```go // Code generated // This file is a generated precompile contract config with stubbed abstract functions. // The file is generated by a template. Please inspect every code and comment in this file before use. package md5 import ( "errors" "fmt" "math/big" "github.com/ava-labs/subnet-evm/accounts/abi" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ava-labs/subnet-evm/vmerrs" _ "embed" "github.com/ethereum/go-ethereum/common" ) const ( // Gas costs for each function. These are set to 1 by default. // You should set a gas cost for each function in your contract. // Generally, you should not set gas costs very low as this may cause your network to be vulnerable to DoS attacks. // There are some predefined gas costs in contract/utils.go that you can use. HashWithMD5GasCost uint64 = 1 /* SET A GAS COST HERE */ ) // CUSTOM CODE STARTS HERE // Reference imports to suppress errors from unused imports. This code and any unnecessary imports can be removed. var ( _ = abi.JSON _ = errors.New _ = big.NewInt _ = vmerrs.ErrOutOfGas _ = common.Big0 ) // Singleton StatefulPrecompiledContract and signatures. var ( // Md5RawABI contains the raw ABI of Md5 contract. //go:embed contract.abi Md5RawABI string Md5ABI = contract.ParseABI(Md5RawABI) Md5Precompile = createMd5Precompile() ) // UnpackHashWithMD5Input attempts to unpack [input] into the string type argument // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackHashWithMD5Input(input []byte) (string, error) { res, err := Md5ABI.UnpackInput("hashWithMD5", input) if err != nil { return "", err } unpacked := *abi.ConvertType(res[0], new(string)).(*string) return unpacked, nil } // PackHashWithMD5 packs [value] of type string into the appropriate arguments for hashWithMD5. // the packed bytes include selector (first 4 func signature bytes). // This function is mostly used for tests. func PackHashWithMD5(value string) ([]byte, error) { return Md5ABI.Pack("hashWithMD5", value) } // PackHashWithMD5Output attempts to pack given hash of type [16]byte // to conform the ABI outputs. func PackHashWithMD5Output(hash [16]byte) ([]byte, error) { return Md5ABI.PackOutput("hashWithMD5", hash) } // UnpackHashWithMD5Output attempts to unpack given [output] into the [16]byte type output // assumes that [output] does not include selector (omits first 4 func signature bytes) func UnpackHashWithMD5Output(output []byte) ([16]byte, error) { res, err := Md5ABI.Unpack("hashWithMD5", output) if err != nil { return [16]byte{}, err } unpacked := *abi.ConvertType(res[0], new([16]byte)).(*[16]byte) return unpacked, nil } func hashWithMD5(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, HashWithMD5GasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the HashWithMD5Input. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackHashWithMD5Input(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output [16]byte // CUSTOM CODE FOR AN OUTPUT packedOutput, err := PackHashWithMD5Output(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // createMd5Precompile returns a StatefulPrecompiledContract with getters and setters for the precompile. func createMd5Precompile() contract.StatefulPrecompiledContract { var functions []*contract.StatefulPrecompileFunction abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{ "hashWithMD5": hashWithMD5, } for name, function := range abiFunctionMap { method, ok := Md5ABI.Methods[name] if !ok { panic(fmt.Errorf("given method (%s) does not exist in the ABI", name)) } functions = append(functions, contract.NewStatefulPrecompileFunction(method.ID, function)) } // Construct the contract with no fallback function. statefulContract, err := contract.NewStatefulPrecompileContract(nil, functions) if err != nil { panic(err) } return statefulContract } ``` # Packing and Unpacking (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/03-unpack-input-pack-output) --- title: Packing and Unpacking description: Learn how to unpack inputs and pack outputs. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- In this first segment of examining the `contract.go` file generated for us, we will go over the packing and unpacking functions in this file. ## The Notion of Packing Those eager to implement the MD5 algorithm might be wondering why we're discussing packing. However, there is good reason to discuss packing, and it comes down to the specification of the `staticcall` function in Solidity. We begin by referring to the example of calling the SHA-256 precompiled contract: ```go (bool ok, bytes memory out) = address(2).staticcall(abi.encode(numberToHash)); ``` As seen above, the `staticcall` function accepts input in bytes format, generated by `abi.encode`, and returns a boolean value indicating success, along with a bytes format output. Therefore, our precompiled contract should be designed to accept and return data in bytes format, involving the packing and unpacking of values. Since packing is a deterministic process, there's no concern about data corruption during translation. However, some preprocessing or postprocessing is necessary to ensure the contract functions correctly. ## Unpacking Inputs In `contract.go`, the function `UnpackHashWithMd5Input` unpacks our data and converts it into a type relevant to us. It takes a byte array as an input and returns a Go string. We will look at more complex precompiles that have multiple functions that may take multiple inputs later. ```go // UnpackHashWithMD5Input attempts to unpack [input] into the string type argument // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackHashWithMD5Input(input []byte) (string, error) { res, err := Md5ABI.UnpackInput("hashWithMD5", input) if err != nil { return "", err } unpacked := *abi.ConvertType(res[0], new(string)).(*string) return unpacked, nil } ``` ## Packing Outputs Ignoring `hashWithMD5` for now, note that whatever value `hashWithMD5` outputs, we will need to postprocess it (i.e. pack it). `PackHashWithMD5Output` does just this, taking in an input of type [16]byte and outputting a byte array which can be returned by `staticcall`. ```go // PackHashWithMd5Output attempts to pack given hash of type [16]byte // to conform the ABI outputs. func PackHashWithMd5Output(hash [16]byte) ([]byte, error) { return Md5ABI.PackOutput("hash_with_md5", hash) } ``` This may seem trivial, but if our Solidity interface defined our function to return a uint or string, the type of our input to this function would differ accordingly. # Implement the Precompile (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/04-implementing-precompile) --- title: Implement the Precompile description: Learn how to implement the Precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Now, we'll implement the logic of the precompile in Go. We'll hash our string using the MD5 algorithm. ## SHA-256 Precompile Implementation Before defining the logic of our MD5 precompile, let's look at the logic of the function `hashWithSHA256` (located in `sha256/contract.go`), which computes the SHA-256 hash of a string: ```go title="sha256/contract.go" import ( "crypto/sha256" //... ) // ... func hashWithSHA256(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, HashWithSHA256GasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the HashWithSHA256Input. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackHashWithSHA256Input(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output [32]byte // CUSTOM CODE FOR AN OUTPUT output = sha256.Sum256([]byte(inputStruct)) packedOutput, err := PackHashWithSHA256Output(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` As you can see, we're performing the following steps: - Line 1: Importing the sha256 function from the crypto library (at the top of the Go file) - Line 15: Unpacking the input to the variable inputStruct. It doesn't make sense that the variable has Struct in its name, but you will see why it is done like this when we have multiple inputs in a later example - Line 24: Calling the sha256 function and assign its result to the output variable - Line 26: Packing the output into a byte array - Line 32: Returning the packed output, the remaining gas and nil, since no error has occurred ## Implementing the MD5 Precompile in `contract.go` Go ahead and implement the `md5/contract.go` for the MD5 precompile. You should only have to write a few lines of code. If you're unsure which function to use, the following documentation might help: [Go Documentation Crypto/md5](https://pkg.go.dev/crypto/md5#Sum) ```go title="md5/contract.go" import ( "crypto/md5" // ... ) // ... func hashWithMD5(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {func hashWithMD5(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, HashWithMD5GasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the HashWithMD5Input. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackHashWithMD5Input(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output [16]byte // CUSTOM CODE FOR AN OUTPUT output = md5.Sum([]byte(inputStruct)) packedOutput, err := PackHashWithMD5Output(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` To solve this task, we did the following things: - Import the md5 function from the crypto library - Unpack the input to a variable inputStruct (It does not make sense that the variable has Struct in its name, but you will see why it is done like this when we have multiple inputs in a later example) - Call the md5 function and assign it's result to the output variable - Pack the output into a byte array - Return the packed output, the remaining gas and nil, since no error has occurred # ConfigKey, ContractAddress, and Genesis (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/05-configkey-and-contractaddr) --- title: ConfigKey, ContractAddress, and Genesis description: Learn how to set ConfigKey, ContractAddress, and update the genesis configuration. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: Terminal --- ## Config Key The precompile config key is used to configure the precompile in the `chainConfig`. It's set in the `module.go` file of our precompile. The generator chooses an initial value that should be sufficient for many cases. In our case: ```go title="md5/module.go" // ConfigKey is the key used in json config files to specify this precompile precompileconfig. // must be unique across all precompiles. const ConfigKey = "md5Config" ``` This key is used for each precompile in the geneis configuration to set the activation timestamp of the precompile. ## Contract Address Each precompile has a unique contract address we can use to call it. This is the address we used earlier to instantiate the precompile in our solidity code or in remix when we interacted with the sha256 precompile. ```go title="md5/module.go" // ContractAddress is the defined address of the precompile contract. // This should be unique across all precompile contracts. // See precompile/registry/registry.go for registered precompile contracts and more information. var ContractAddress = common.HexToAddress("{ASUITABLEHEXADDRESS}") // SET A SUITABLE HEX ADDRESS HERE ``` The `0x01` range is reserved for precompiles added by Ethereum. The `0x02` range is reserved for precompiles provided by Avalanche. The `0x03` range is reserved for custom precompiles. Lets set our contract address to `0x0300000000000000000000000000000000000002` ```go title="md5/module.go" var ContractAddress = common.HexToAddress("0x0300000000000000000000000000000000000002") // SET A SUITABLE HEX ADDRESS HERE ``` ## Update Genesis Configuration Now that the `ConfigKey` and `ContractAddress` are set, we need to update the genesis configuration to register the precompile. ```json title=".devcontainer/genesis-example.json" { "config": { "chainId": 99999, "homesteadBlock": 0, "eip150Block": 0, "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "istanbulBlock": 0, "muirGlacierBlock": 0, "subnetEVMTimestamp": 0, "feeConfig": { "gasLimit": 20000000, "minBaseFee": 1000000000, "targetGas": 100000000, "baseFeeChangeDenominator": 48, "minBlockGasCost": 0, "maxBlockGasCost": 10000000, "targetBlockRate": 2, "blockGasCostStep": 500000 }, "sha256Config": { "blockTimestamp": 0 }, "md5Config": { // [!code highlight:3] "blockTimestamp": 0 } }, "alloc": { "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { "balance": "0x52B7D2DCC80CD2E4000000" } }, "nonce": "0x0", "timestamp": "0x0", "extraData": "0x00", "gasLimit": "0x1312D00", "difficulty": "0x0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } ``` # Register Your Precompile (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/06-register-precompile) --- title: Register Your Precompile description: Learn how to register your precompile. updated: 2024-09-27 authors: [owenwahlgren] icon: Terminal --- The next step in developing our precompile is to **register it with precompile-evm**. Take a look at `plugin/main.go`. You should see the following file: ```go title="plugin/main.go" // (c) 2019-2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package main import ( "fmt" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ava-labs/subnet-evm/plugin/runner" // Each precompile generated by the precompilegen tool has a self-registering init function // that registers the precompile with the subnet-evm. Importing the precompile package here // will cause the precompile to be registered with the subnet-evm. // ADD YOUR PRECOMPILE HERE //_ "github.com/ava-labs/precompile-evm/{yourprecompilepkg}" ) const Version = "v0.1.4" func main() { versionString := fmt.Sprintf("Precompile-EVM/%s Avalanche L1-EVM/%s [AvalancheGo=%s, rpcchainvm=%d]", Version, evm.Version, version.Current, version.RPCChainVMProtocol) runner.Run(versionString) } ``` As of now, we do not have any precompile registered. To reigster a precompile, simply import the precompile package in the `plugin/main.go` file. ```go title="plugin/main.go" // (c) 2019-2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package main import ( "fmt" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ava-labs/subnet-evm/plugin/runner" // Each precompile generated by the precompilegen tool has a self-registering init function // that registers the precompile with the subnet-evm. Importing the precompile package here // will cause the precompile to be registered with the subnet-evm. _ "github.com/ava-labs/precompile-evm/sha256"// [!code highlight:2] _ "github.com/ava-labs/precompile-evm/md5" ) const Version = "v0.1.4" func main() { versionString := fmt.Sprintf("Precompile-EVM/%s Avalanche L1-EVM/%s [AvalancheGo=%s, rpcchainvm=%d]", Version, evm.Version, version.Current, version.RPCChainVMProtocol) runner.Run(versionString) } ``` # Build and Run (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/07-build-and-run) --- title: Build and Run description: Learn how to build and run your custom VM on a local network. updated: 2024-09-27 authors: [ashucoder9, owenwahlgren] icon: Terminal --- Time to build and run your customized EVM. Follow the steps below to build and run your custom VM on a local network. ### Build Your Custom VM There's a simple build script in the Precompile-EVM we can utilize to build. First, make sure you are in the root folder of you Precompile-EVM: ```bash cd $GOPATH/src/github.com/ava-labs/precompile-evm ``` Then run the command to initiate the build script: ```bash ./scripts/build.sh ``` If you do not see any error, the build was successful. ### Create your blockchain configuration You can run you customized Precompile-EVM by using the Avalanche CLI. First, create the configuration for your blockchain with custom VM. ```bash avalanche blockchain create myblockchain \ --custom \ --vm $VM_PATH \ --genesis "./.devcontainer/genesis-example.json" \ --force \ --sovereign=false \ --evm-token "TOK" \ --warp \ --icm ``` ### Launch L1 with you customized EVM Next, launch the Avalanche L1 with your custom VM: ```bash avalanche blockchain deploy myblockchain --local ``` After around 1 minute the blockchain should have been created and some more output should appear in the terminal. You'll also see the RPC URL of your blockchain in the terminal. # Interact with Precompile (/academy/avalanche-l1/customizing-evm/07-hash-function-precompile/08-interact-with-md5) --- title: Interact with Precompile description: Interact with the MD5 precompile we just deployed. updated: 2024-08-27 authors: [owenwahlgren] icon: Terminal --- ## Call Precompile from Foundry Now we will call our MD5 precompile to generate a bytes16 hash of the input string. **MD5 Precompile Address:** `0x0300000000000000000000000000000000000002` ```bash cast call --rpc-url myblockchain --private-key $PK 0x0300000000000000000000000000000000000002 "hashWithMD5(string)(bytes16)" "test" ``` You should see the bytes16 hash of the input string `test` as the output. ```bash 0x098f6bcd4621d373cade4e832627b4f6 ``` # What are Stateful Precompiles? (/academy/avalanche-l1/customizing-evm/09-stateful-precompiles/00-intro) --- title: What are Stateful Precompiles? description: Learn about Stateful Precompiles in Avalanche L1 EVM. updated: 2024-05-31 authors: [ashucoder9] icon: Book --- When building the MD5 and Calculator precompiles, we emphasized their behavior. We focused on building precompiles that developers could call in Solidity to perform some algorithm and then simply return a result. However, one aspect that we have yet to explore is the statefulness of precompiles. Simply put, precompiles can store data which is persistent. To understand how this is possible, recall the interface that our precompile needed to implement: ```go // StatefulPrecompiledContract is the interface for executing a precompiled contract type StatefulPrecompiledContract interface { // Run executes the precompiled contract. Run(accessibleState AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) } ``` We also examined all parameters except for the `AccessibleState` parameter. As the name suggests, this parameter lets us access the blockchain's state. Looking at the interface of `AccessibleState`, we have the following: ```go // AccessibleState defines the interface exposed to stateful precompile contracts type AccessibleState interface { GetStateDB() StateDB GetBlockContext() BlockContext GetSnowContext() *snow.Context } ``` Looking closer, we see that `AccessibleState` gives us access to **StateDB**, which is used to store the state of the EVM. However, as we will see throughout this section, `AccessibleState` also gives us access to other useful parameters, such as `BlockContext` and `snow.Context`. ## StateDB The parameter we will use the most when it comes to stateful precompiles, StateDB, is a key-value mapping that maps: - **Key**: a tuple consisting of an address and the storage key of the type Hash - **Value**: any data encoded in a Hash, also called a word in the EVM A Hash in go-ethereum is a 32-byte array. In this context, we do not refer to hashing in the cryptographic sense. Rather, "hashing a value" means encoding it to a hash, a 32-byte array usually represented in hexadecimal digits in Ethereum. ```go const ( // HashLength is the expected length of the hash HashLength = 32 ) // Hash represents the 32 byte of arbitrary data. type Hash [HashLength]byte // Example of a data encoded in a Hash: // 0x00000000000000000000000000000000000000000048656c6c6f20576f726c64 Below is the interface of StateDB: // StateDB is the interface for accessing EVM state type StateDB interface { GetState(common.Address, common.Hash) common.Hash SetState(common.Address, common.Hash, common.Hash) SetNonce(common.Address, uint64) GetNonce(common.Address) uint64 GetBalance(common.Address) *big.Int AddBalance(common.Address, *big.Int) CreateAccount(common.Address) Exist(common.Address) bool AddLog(addr common.Address, topics []common.Hash, data []byte, blockNumber uint64) GetPredicateStorageSlots(address common.Address) ([]byte, bool) Suicide(common.Address) bool Finalise(deleteEmptyObjects bool) Snapshot() int RevertToSnapshot(int) } ``` As you can see in the interface of the **StateDB**, the two functions for writing to and reading from the EVM state all work with the Hash type. 1. The function **GetState** takes an address and a Hash (the storage key) as inputs and returns the Hash stored at that slot. 2. The function **SetState** takes an address, a Hash (the storage key), and another Hash (data to be stored) as inputs. We can also see that the **StateDB** interface allows us to read and write account balances of the native token (via GetBalance/SetBalance), check whether an account exists (via Exist), and some other methods. ## BlockContext `BlockContext` provides info about the current block. In particular, we can get the current block number and the current block timestamp. Below is the interface of `BlockContext`: ```go // BlockContext defines an interface that provides information to a stateful precompile about the current block. // The BlockContext may be provided during both precompile activation and execution. type BlockContext interface { Number() *big.Int Timestamp() *big.Int } ``` An example of how we could leverage `BlockContext` in precompiles: *building a voting precompile that only accepts votes within a certain time frame*. ## snow.Context `snow.Context` gives us info regarding the environment of the precompile. In particular, `snow.Context` tells us about the state of the network the precompile is hosted on. Below is the interface of `snow.Context`: ```go // Context is information about the current execution. // [NetworkID] is the ID of the network this context exists within. // [ChainID] is the ID of the chain this context exists within. // [NodeID] is the ID of this node type Context struct { NetworkID uint32 Avalanche L1ID ids.ID ChainID ids.ID NodeID ids.NodeID PublicKey *bls.PublicKey XChainID ids.ID CChainID ids.ID AVAXAssetID ids.ID Log logging.Logger Lock sync.RWMutex Keystore keystore.BlockchainKeystore SharedMemory atomic.SharedMemory BCLookup ids.AliaserReader Metrics metrics.OptionalGatherer WarpSigner warp.Signer // snowman++ attributes ValidatorState validators.State // interface for P-Chain validators // Chain-specific directory where arbitrary data can be written ChainDataDir string } ``` # Interacting with StringStore Precompile (/academy/avalanche-l1/customizing-evm/09-stateful-precompiles/01-interacting-with-precompile) --- title: Interacting with StringStore Precompile description: Learn how to interact with StringStore Stateful precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Rather than understanding the statefulness of precompiles only in theory, we can also play around with an example of a stateful precompile to learn how they work in practice. In this section, we'll interact with the **StringStore** precompile, a precompiled smart contract that stores a string. ## Checking the Genesis JSON As part of all Avalanche Academy branches, your Precompile-EVM should include the **StringStore/** folder along with all other relevant files for StringStore to be recognized by Precompile-EVM. This includes a genesis JSON for StringStore. Go to `tests/precompile/genesis/` and double-check that you have a `StringStore.json` folder in said directory. This JSON files will instantiate both the **SHA256** precompile and **StringStore**. ```json title="StringStore.json" { "config": { "chainId": 99999, // ... "stringStoreConfig" : { "blockTimestamp": 0, "defaultString": "Cornell" } }, // ... } ``` ## Building the EVM with StringStore Precompile The **avalanche-academy-start** branch already contains the StringStore and SHA256 precompile. If you are working from that branch, you can simply use the built binary from your latest exercise without making any changes. To verify, check if the **stringstore** directory is in your workspace and if the precompile is noted in the `plugin/main.go` file. If not, switch to the **avalanche-academy-start** branch and build the VM there. ## Start the Avalanche Network Use the Avalanche-CLI to start the server and the network. Use the provided genesis file `stringstore.json` mentioned above when you start the network. If all goes well, you will have successfully deployed a blockchain containing both the StringStore and SHA256 precompile. ## Connecting Core Similar to previous chapters, navigate to the **Add Network** section in the Core Wallet. You can find the RPC URL in the Avalanche-CLI logs or by executing the command: `avalanche blockchain list --deployed` Note: Make sure the RPC URL ends with **/rpc**. The RPC URL should look something like this: http://127.0.0.1:9650/ext/bc/P9nKPGPoAfFGkdvD3Ac6YxZieaG8ahpbR9xZosrWNPbJCzByu/rpc Once you have added the blockchain network, switch Core Wallet to your blockchain. ## Interact through Remix We will now load in the Solidity interface letting us interact with the **StringStore** precompile. To do this, open the link below, which will open a Remix workspace containing the StringStore precompile: [Workspace](https://remix.ethereum.org/#url=https://github.com/ava-labs/precompile-evm/blob/avalanche-academy-start/contracts/contracts/interfaces/IStringStore.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.26+commit.8a97fa7a.js) As usual, we will need to compile our Solidity interface. 1. Click the **Solidity** logo on the left sidebar. 2. In the new page, you will see a **Compile IStringStore.sol** button. After clicking the button, a green checkmark should appear next to the Solidity logo. 3. Next, go to the **Environment** tab and select the **Injected Provider** option. If successful, a text saying **Custom [99999] Network** will appear below. If not, change your network. 4. Enter the precompile address (find it in `precompile/stringstore/module.go`) and click **At Address**. First, we will call the `getString` function. By default, `getString` will return whatever was specified in the genesis JSON. Since we set our StringStore precompile to store the string **Cornell**, it'll return this value. ![](https://qizat5l3bwvomkny.public.blob.vercel-storage.com/builders-hub/course-images/customizing-evm/48-THf1urUZpFdGgScCm6ZhNWVQITtsiq.png) As you might have noticed, we can also set the string that **StringStore** stores. For example, if we wanted to change the string to Avalanche, we would type Avalanche in the box next to `setString` method, press the `setString` button, and then you would see in the Remix terminal a message displaying the success of your transaction. If we call `getString` again, you will see that the string has been changed to Avalanche. Congrats, you've just interacted with the stateful **StringStore** precompile 🎉 In contrast to the precompiles we have built earlier, the StringStore precompile has access to the EVM state. This way we can utilize precompile not only to perform calculations, but also persist something to the EVM state. This allows us to move even larger portions of our dApp from the solidity smart contract layer to the precompile layer. # Overview (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/00-intro) --- title: Overview description: Learn how to create a Calculator precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; ## Reference Implementation In this section, we will showcase how to build a more complex precompile that offers selected simple math operations. Our calculator will support the following operations: 1. Add two numbers and return the result (3 + 5 = 8) 2. Get the next two greater numbers (7 => 8, 9) 3. Repeat a string x times (4, b => bbbb) This is somewhat odd calculator and real-life usability might not be the best, but as you will see in a bit the operations have been chosen to demonstrate some different scenarios we might face while building precompiles. ## What You Are Building Similar to the Calculator precompile, you will be building a precompile called **CalculatorPlus** which contains the following mathematical functions: - **Powers of Three**: takes in as input an integer base; returns the square, cube, and 4th power of the input - **Modulo+**: takes in as input two arguments: the dividend and the divisor. Returns how many times the dividend fits in the divisor, and the remainder. - **Simplify Fraction**: takes in two arguments: the numerator and the denominator. Returns the simplfied version of the fraction (if the denominator is 0, we return 0) ## Overview of Steps Compared to the process before, we will now also add tests for our precompile. Here's a quick overview of the steps we're going to follow: Create a Solidity interface for the precompile Generate the ABI Write the precompile code in Go Configure and register the precompile Add and run tests Build and run your customized EVM Connect Remix to your customized EVM and interact with the precompile This tutorial will help you understand how to create more complex precompiles. Let's begin! # Create Solidity Interface (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/01-create-solidity-interface) --- title: Create Solidity Interface description: Learn how to create the Solidity interface for your calculator precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Just like in the MD5 section, we will start off by first demonstrating the Solidity interface for the Calculator precompile before guiding you on how to build the **CalculatorPlus** precompile. To start, let's take a look at the Calculator Solidity interface: ```solidity title="contracts/contracts/interfaces/ICalculator.sol" // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ICalculator { function add(uint value1, uint value2) external view returns(uint result); function nextTwo(uint value1) external view returns(uint result1, uint result2); function repeat(uint times, string memory text) external view returns(string memory result); } ``` With this in mind, let's define the CalculatorPlus Solidity interface. Your interface should have the following three functions: 1. `powOfThree`: takes in an unsigned integer base, and returns three unsigned integers named secondPow, thirdPow, fourthPow . 2. `moduloPlus`: takes in unsigned integers dividend and divisor as input, and returns two unsigned integers named multiple and remainder . 3. `simplFrac`: takes in unsigned integers named numerator and denominator, and returns two unsigned integers named simplNum and simplDenom ```solidity title="solidity/interfaces/ICalculatorPlus.sol" // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ICalculatorPlus { function powOfThree(uint256 base) external view returns(uint256 secondPow, uint256 thirdPow, uint256 fourthPow); function moduloPlus(uint256 dividend, uint256 divisor) external view returns(uint256 multiple, uint256 remainder); function simplFrac(uint256 numerator, uint256 denominator) external view returns(uint256 simplNum, uint256 simplDenom); } ``` ## Generate the ABI Now that we have an interface of our precompile, let's create an ABI of our Solidity interface. Open the terminal (control + \`), change to the `/contracts` directory and run the following command to compile the solidity interface to the ABI: ```bash # Go to the upper contracts directory of your project cd contracts # Compile ICalculatorPlus.sol to ABI npx solc@latest --abi ./contracts/interfaces/ICalculatorPlus.sol -o ./abis --base-path . --include-path ./node_modules # Rename using this script or manually mv ./abis/contracts_interfaces_ICalculatorPlus_sol_ICalculatorPlus.abi ./abis/ICalculatorPlus.abi ``` Now you should have a file called `ICalculatorPlus.abi` in the folder `/contracts/abis` with the following content: ```json title="/contracts/abis/ICalculatorPlus.abi" [ { "inputs": [ { "internalType": "uint256", "name": "dividend", "type": "uint256" }, { "internalType": "uint256", "name": "divisor", "type": "uint256" } ], "name": "moduloPlus", "outputs": [ { "internalType": "uint256", "name": "multiple", "type": "uint256" }, { "internalType": "uint256", "name": "remainder", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "base", "type": "uint256" } ], "name": "powOfThree", "outputs": [ { "internalType": "uint256", "name": "secondPow", "type": "uint256" }, { "internalType": "uint256", "name": "thirdPow", "type": "uint256" }, { "internalType": "uint256", "name": "fourthPow", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "numerator", "type": "uint256" }, { "internalType": "uint256", "name": "denominator", "type": "uint256" } ], "name": "simplFrac", "outputs": [ { "internalType": "uint256", "name": "simplNum", "type": "uint256" }, { "internalType": "uint256", "name": "simplDenom", "type": "uint256" } ], "stateMutability": "view", "type": "function" } ] ``` # Generating the Precompile (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/02-generating-precompile) --- title: Generating the Precompile description: Learn how to generating the precompile updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- In this step, we will again utilize the precompile generation script to generate all the Go files based on the ABI for your calculator. ## Run Generation Script Change to the root directory of your precompile-evm project and run the command to generate the go files: ```bash # Change to root cd .. # Generate go files ./scripts/generate_precompile.sh --abi ./contracts/abis/ICalculatorPlus.abi --type Calculatorplus --pkg calculatorplus --out ./calculatorplus ``` Now you should have a new directory called `calculatorplus` in the root directory of your project. If you check our the generated contract.go file you will see right away that it is much longer than in our hash function precompile from earlier. This is due to the fact that our calculator precompile has more functions and parameters. Browse through the code and see if you can spot the new elements: ```go title="contract.go" // Code generated // This file is a generated precompile contract config with stubbed abstract functions. // The file is generated by a template. Please inspect every code and comment in this file before use. package calculatorplus import ( "errors" "fmt" "math/big" "github.com/ava-labs/subnet-evm/accounts/abi" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ava-labs/subnet-evm/vmerrs" _ "embed" "github.com/ethereum/go-ethereum/common" ) const ( // Gas costs for each function. These are set to 1 by default. // You should set a gas cost for each function in your contract. // Generally, you should not set gas costs very low as this may cause your network to be vulnerable to DoS attacks. // There are some predefined gas costs in contract/utils.go that you can use. ModuloPlusGasCost uint64 = 1 /* SET A GAS COST HERE */ PowOfThreeGasCost uint64 = 1 /* SET A GAS COST HERE */ SimplFracGasCost uint64 = 1 /* SET A GAS COST HERE */ ) // CUSTOM CODE STARTS HERE // Reference imports to suppress errors from unused imports. This code and any unnecessary imports can be removed. var ( _ = abi.JSON _ = errors.New _ = big.NewInt _ = vmerrs.ErrOutOfGas _ = common.Big0 ) // Singleton StatefulPrecompiledContract and signatures. var ( // CalculatorplusRawABI contains the raw ABI of Calculatorplus contract. //go:embed contract.abi CalculatorplusRawABI string CalculatorplusABI = contract.ParseABI(CalculatorplusRawABI) CalculatorplusPrecompile = createCalculatorplusPrecompile() ) type ModuloPlusInput struct { Dividend *big.Int Divisor *big.Int } type ModuloPlusOutput struct { Multiple *big.Int Remainder *big.Int } type PowOfThreeOutput struct { SecondPow *big.Int ThirdPow *big.Int FourthPow *big.Int } type SimplFracInput struct { Numerator *big.Int Denominator *big.Int } type SimplFracOutput struct { SimplNum *big.Int SimplDenom *big.Int } // UnpackModuloPlusInput attempts to unpack [input] as ModuloPlusInput // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackModuloPlusInput(input []byte) (ModuloPlusInput, error) { inputStruct := ModuloPlusInput{} err := CalculatorplusABI.UnpackInputIntoInterface(&inputStruct, "moduloPlus", input) return inputStruct, err } // PackModuloPlus packs [inputStruct] of type ModuloPlusInput into the appropriate arguments for moduloPlus. func PackModuloPlus(inputStruct ModuloPlusInput) ([]byte, error) { return CalculatorplusABI.Pack("moduloPlus", inputStruct.Dividend, inputStruct.Divisor) } // PackModuloPlusOutput attempts to pack given [outputStruct] of type ModuloPlusOutput // to conform the ABI outputs. func PackModuloPlusOutput(outputStruct ModuloPlusOutput) ([]byte, error) { return CalculatorplusABI.PackOutput("moduloPlus", outputStruct.Multiple, outputStruct.Remainder, ) } // UnpackModuloPlusOutput attempts to unpack [output] as ModuloPlusOutput // assumes that [output] does not include selector (omits first 4 func signature bytes) func UnpackModuloPlusOutput(output []byte) (ModuloPlusOutput, error) { outputStruct := ModuloPlusOutput{} err := CalculatorplusABI.UnpackIntoInterface(&outputStruct, "moduloPlus", output) return outputStruct, err } func moduloPlus(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, ModuloPlusGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the ModuloPlusInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackModuloPlusInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output ModuloPlusOutput // CUSTOM CODE FOR AN OUTPUT packedOutput, err := PackModuloPlusOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // UnpackPowOfThreeInput attempts to unpack [input] into the *big.Int type argument // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackPowOfThreeInput(input []byte) (*big.Int, error) { res, err := CalculatorplusABI.UnpackInput("powOfThree", input) if err != nil { return new(big.Int), err } unpacked := *abi.ConvertType(res[0], new(*big.Int)).(**big.Int) return unpacked, nil } // PackPowOfThree packs [base] of type *big.Int into the appropriate arguments for powOfThree. // the packed bytes include selector (first 4 func signature bytes). // This function is mostly used for tests. func PackPowOfThree(base *big.Int) ([]byte, error) { return CalculatorplusABI.Pack("powOfThree", base) } // PackPowOfThreeOutput attempts to pack given [outputStruct] of type PowOfThreeOutput // to conform the ABI outputs. func PackPowOfThreeOutput(outputStruct PowOfThreeOutput) ([]byte, error) { return CalculatorplusABI.PackOutput("powOfThree", outputStruct.SecondPow, outputStruct.ThirdPow, outputStruct.FourthPow, ) } // UnpackPowOfThreeOutput attempts to unpack [output] as PowOfThreeOutput // assumes that [output] does not include selector (omits first 4 func signature bytes) func UnpackPowOfThreeOutput(output []byte) (PowOfThreeOutput, error) { outputStruct := PowOfThreeOutput{} err := CalculatorplusABI.UnpackIntoInterface(&outputStruct, "powOfThree", output) return outputStruct, err } func powOfThree(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, PowOfThreeGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the PowOfThreeInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackPowOfThreeInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output PowOfThreeOutput // CUSTOM CODE FOR AN OUTPUT packedOutput, err := PackPowOfThreeOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // UnpackSimplFracInput attempts to unpack [input] as SimplFracInput // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackSimplFracInput(input []byte) (SimplFracInput, error) { inputStruct := SimplFracInput{} err := CalculatorplusABI.UnpackInputIntoInterface(&inputStruct, "simplFrac", input) return inputStruct, err } // PackSimplFrac packs [inputStruct] of type SimplFracInput into the appropriate arguments for simplFrac. func PackSimplFrac(inputStruct SimplFracInput) ([]byte, error) { return CalculatorplusABI.Pack("simplFrac", inputStruct.Numerator, inputStruct.Denominator) } // PackSimplFracOutput attempts to pack given [outputStruct] of type SimplFracOutput // to conform the ABI outputs. func PackSimplFracOutput(outputStruct SimplFracOutput) ([]byte, error) { return CalculatorplusABI.PackOutput("simplFrac", outputStruct.SimplNum, outputStruct.SimplDenom, ) } // UnpackSimplFracOutput attempts to unpack [output] as SimplFracOutput // assumes that [output] does not include selector (omits first 4 func signature bytes) func UnpackSimplFracOutput(output []byte) (SimplFracOutput, error) { outputStruct := SimplFracOutput{} err := CalculatorplusABI.UnpackIntoInterface(&outputStruct, "simplFrac", output) return outputStruct, err } func simplFrac(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, SimplFracGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the SimplFracInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackSimplFracInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output SimplFracOutput // CUSTOM CODE FOR AN OUTPUT packedOutput, err := PackSimplFracOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // createCalculatorplusPrecompile returns a StatefulPrecompiledContract with getters and setters for the precompile. func createCalculatorplusPrecompile() contract.StatefulPrecompiledContract { var functions []*contract.StatefulPrecompileFunction abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{ "moduloPlus": moduloPlus, "powOfThree": powOfThree, "simplFrac": simplFrac, } for name, function := range abiFunctionMap { method, ok := CalculatorplusABI.Methods[name] if !ok { panic(fmt.Errorf("given method (%s) does not exist in the ABI", name)) } functions = append(functions, contract.NewStatefulPrecompileFunction(method.ID, function)) } // Construct the contract with no fallback function. statefulContract, err := contract.NewStatefulPrecompileContract(nil, functions) if err != nil { panic(err) } return statefulContract } ``` # Unpacking Multiple Inputs & Packing Multiple Outputs (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/03-unpacking-and-packing) --- title: Unpacking Multiple Inputs & Packing Multiple Outputs description: Learn how to unpack multiple inputs and packing multiple outputs. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## Unpack/Pack Functions For Each Operation As with the MD5 precompile, the generator created the pack/unpack functions for each operation of our Calculator and CalculatorPlus precompiles. However, you might have noticed that the generator also created some struct in each respective `contract.go` file. In the case of CalculatorPrecompile, we have: ```go title="contract.go" type AddInput struct { Value1 *big.Int Value2 *big.Int } type NextTwoOutput struct { Result1 *big.Int Result2 *big.Int } type RepeatInput struct { Times *big.Int Text string } ``` The generator creates these structs whenever a function defined in the respective Solidity interface has more than one input or more than one output. These multiple values are now stored together via structs. ## Unpacking Multiple Inputs To understand how unpacking works when structs are introduced, let's take a look at the add function in the Calculator precompile, which takes in two inputs, and the nextTwo, which takes in only one input. ```go // UnpackAddInput attempts to unpack [input] as AddInput // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackAddInput(input []byte) (AddInput, error) { inputStruct := AddInput{} err := CalculatorABI.UnpackInputIntoInterface(&inputStruct, "add", input) return inputStruct, err } // UnpackNextTwoInput attempts to unpack [input] into the *big.Int type argument // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackNextTwoInput(input []byte) (*big.Int, error) { res, err := CalculatorABI.UnpackInput("nextTwo", input) if err != nil { return big.NewInt(0), err } unpacked := *abi.ConvertType(res[0], new(*big.Int)).(**big.Int) return unpacked, nil } ``` As you can see, both unpacker functions take a byte array as an input but the `UnpackAddInput` returns a value of the type `AddInput` (which contains two `big.Int` values) and `UnpackNextTwoInput` returns a value of the type `big.Int`. In each case, the respective unpacking function takes in a byte array as an input. However, `UnpackAddInput` returns a value of the type `AddInput`, which is a struct that contains two `big.Int` values. `UnpackNextTwo`, meanwhile, returns an value of type `big.Int`. To demonstrate how one could use the output of unpacking functions like `UnpackAddInput`, below is the implementation of the add function of the Calculator precompile: ```go func add(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, AddGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the AddInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackAddInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE var output *big.Int // CUSTOM CODE FOR AN OUTPUT output = big.NewInt(0).Add(inputStruct.Value1, inputStruct.Value2) packedOutput, err := PackAddOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` ## Packing Multiple Outputs To understand how structs affect the packing of outputs, lets refer to `PackNextTwoOutput` for the Calculator precompile: ```go // PackHashWithMd5Output attempts to pack given hash of type [16] // PackNextTwoOutput attempts to pack given [outputStruct] of type NextTwoOutput // to conform the ABI outputs. func PackNextTwoOutput(outputStruct NextTwoOutput) ([]byte, error) { return CalculatorABI.PackOutput("nextTwo", outputStruct.Result1, outputStruct.Result2, ) } ``` Notice here that even though we are no longer working with singular types, the generator still is able to pack our struct so that it is of the type bytes. As a result, we are able to return the packed version of any `NextTwoOutput` struct as bytes at the end of nextTwo. # Implementing Precompile (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/04-implementing-precompile) --- title: Implementing Precompile description: Learn how to implement the precompile in `contract.go` updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- In this section, we will go define the logic for our CalculatorPlus precompile; in particular, we want to add the logic for the following three functions: `powOfThree`, `moduloPlus`, and `simplFrac`. For those worried about this section - don't be! Our solution only added 12 lines of code to `contract.go`. ## Looking at Calculator Before we define the logic of CalculatorPlus, we first will examine the implementation of the Calculator precompile: ```go func add(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, AddGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the AddInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackAddInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output *big.Int // CUSTOM CODE FOR AN OUTPUT output = big.NewInt(0).Add(inputStruct.Value1, inputStruct.Value2) packedOutput, err := PackAddOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // ... func repeat(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, RepeatGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the RepeatInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackRepeatInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output string // CUSTOM CODE FOR AN OUTPUT output = strings.Repeat(inputStruct.Text, int(inputStruct.Times.Int64())) packedOutput, err := PackRepeatOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // ... func nextTwo(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, NextTwoGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the NextTwoInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackNextTwoInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output NextTwoOutput // CUSTOM CODE FOR AN OUTPUT output.Result1 = big.NewInt(0).Add(inputStruct, big.NewInt(1)) output.Result2 = big.NewInt(0).Add(inputStruct, big.NewInt(2)) packedOutput, err := PackNextTwoOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` Although the code snippet above may be long, you might notice that we added only four lines of code to the autogenerated code provided to us by Precompile-EVM! In particular, we only added lines 19, 48, 79, and 80. In general, note the following: - Structs vs Singular Values: make sure to keep track which inputs/outputs are structs and which one are values like big.Int. As an example, in `nextTwo`, we are dealing with a big.Int type input. However, in repeat, we are passed in a input of type struct RepeatInput. - Documentation: for both Calculator and CalculatorPlus, the big package documentation is of great reference: https://pkg.go.dev/math/big Now that we have looked at the implementation for the Calculator precompile, its time you define the CalculatorPlus precompile! ## Implementing moduloPlus We start by looking at the starter code for `moduloPlus`: ```go func moduloPlus(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, ModuloPlusGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the ModuloPlusInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackModuloPlusInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output ModuloPlusOutput // CUSTOM CODE FOR AN OUTPUT packedOutput, err := PackModuloPlusOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` We want to note the following: - `inputStruct` is the input that we want to work with (i.e. `inputStruct` contains the two numbers that we want to use for the modulo calculation) - All of our code will go after line 15 - We want the struct output to contain the result of our modulo operation (the struct will contain the multiple and remainder) With this in mind, try to implement `moduloPlus`. ```go func moduloPlus(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {func moduloPlus(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, ModuloPlusGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the ModuloPlusInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackModuloPlusInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output ModuloPlusOutput // CUSTOM CODE FOR AN OUTPUT output.Multiple, output.Remainder = big.NewInt(0).DivMod(inputStruct.Dividend, inputStruct.Divisor, output.Remainder) packedOutput, err := PackModuloPlusOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` ## Implementing powOfThree Likewise, for `powOfThree`, we want to define the logic of the function in the custom code section. However, note that while we are working with an output struct, our input is a singular value. With this in mind, take a crack at implementing `powOfThree`: ```go func powOfThree(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {func powOfThree(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, PowOfThreeGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the PowOfThreeInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackPowOfThreeInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output PowOfThreeOutput // CUSTOM CODE FOR AN OUTPUT output.SecondPow = big.NewInt(0).Exp(inputStruct, big.NewInt(2), nil) output.ThirdPow = big.NewInt(0).Exp(inputStruct, big.NewInt(3), nil) output.FourthPow = big.NewInt(0).Exp(inputStruct, big.NewInt(4), nil) packedOutput, err := PackPowOfThreeOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` ## Implementing simplFrac For implementing `simplFrac`, note the following: - The documentation for the big package will be of use here - Remember to take care of the case when the denominator is 0 ```go func simplFrac(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {func simplFrac(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, SimplFracGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the SimplFracInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackSimplFracInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output SimplFracOutput // CUSTOM CODE FOR AN OUTPUT // If denominator is 0, return both 0 if inputStruct.Denominator.Cmp(big.NewInt(0)) == 0 { output.SimplDenom = big.NewInt(0) output.SimplNum = big.NewInt(0) } else { // First, find common denominator var gcd big.Int gcd.GCD(nil, nil, inputStruct.Numerator, inputStruct.Denominator) // Now, simplify fraction output.SimplNum = big.NewInt(0).Div(inputStruct.Numerator, &gcd) output.SimplDenom = big.NewInt(0).Div(inputStruct.Denominator, &gcd) } packedOutput, err := PackSimplFracOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` ## Final Solution Below is the final solution for the CalculatorPlus precompile: ```go // Code generated // This file is a generated precompile contract config with stubbed abstract functions. // The file is generated by a template. Please inspect every code and comment in this file before use. package calculatorplus import ( "errors" "fmt" "math/big" "github.com/ava-labs/subnet-evm/accounts/abi" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ava-labs/subnet-evm/vmerrs" _ "embed" "github.com/ethereum/go-ethereum/common" ) const ( // Gas costs for each function. These are set to 1 by default. // You should set a gas cost for each function in your contract. // Generally, you should not set gas costs very low as this may cause your network to be vulnerable to DoS attacks. // There are some predefined gas costs in contract/utils.go that you can use. ModuloPlusGasCost uint64 = 1 /* SET A GAS COST HERE */ PowOfThreeGasCost uint64 = 1 /* SET A GAS COST HERE */ SimplFracGasCost uint64 = 1 /* SET A GAS COST HERE */ ) // CUSTOM CODE STARTS HERE // Reference imports to suppress errors from unused imports. This code and any unnecessary imports can be removed. var ( _ = abi.JSON _ = errors.New _ = big.NewInt _ = vmerrs.ErrOutOfGas _ = common.Big0 ) // Singleton StatefulPrecompiledContract and signatures. var ( // CalculatorplusRawABI contains the raw ABI of Calculatorplus contract. //go:embed contract.abi CalculatorplusRawABI string CalculatorplusABI = contract.ParseABI(CalculatorplusRawABI) CalculatorplusPrecompile = createCalculatorplusPrecompile() ) type ModuloPlusInput struct { Dividend *big.Int Divisor *big.Int } type ModuloPlusOutput struct { Multiple *big.Int Remainder *big.Int } type PowOfThreeOutput struct { SecondPow *big.Int ThirdPow *big.Int FourthPow *big.Int } type SimplFracInput struct { Numerator *big.Int Denominator *big.Int } type SimplFracOutput struct { SimplNum *big.Int SimplDenom *big.Int } // UnpackModuloPlusInput attempts to unpack [input] as ModuloPlusInput // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackModuloPlusInput(input []byte) (ModuloPlusInput, error) { inputStruct := ModuloPlusInput{} err := CalculatorplusABI.UnpackInputIntoInterface(&inputStruct, "moduloPlus", input) return inputStruct, err } // PackModuloPlus packs [inputStruct] of type ModuloPlusInput into the appropriate arguments for moduloPlus. func PackModuloPlus(inputStruct ModuloPlusInput) ([]byte, error) { return CalculatorplusABI.Pack("moduloPlus", inputStruct.Dividend, inputStruct.Divisor) } // PackModuloPlusOutput attempts to pack given [outputStruct] of type ModuloPlusOutput // to conform the ABI outputs. func PackModuloPlusOutput(outputStruct ModuloPlusOutput) ([]byte, error) { return CalculatorplusABI.PackOutput("moduloPlus", outputStruct.Multiple, outputStruct.Remainder, ) } // UnpackModuloPlusOutput attempts to unpack [output] as ModuloPlusOutput // assumes that [output] does not include selector (omits first 4 func signature bytes) func UnpackModuloPlusOutput(output []byte) (ModuloPlusOutput, error) { outputStruct := ModuloPlusOutput{} err := CalculatorplusABI.UnpackIntoInterface(&outputStruct, "moduloPlus", output) return outputStruct, err } func moduloPlus(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, ModuloPlusGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the ModuloPlusInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackModuloPlusInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output ModuloPlusOutput // CUSTOM CODE FOR AN OUTPUT output.Multiple, output.Remainder = big.NewInt(0).DivMod(inputStruct.Dividend, inputStruct.Divisor, output.Remainder) packedOutput, err := PackModuloPlusOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // UnpackPowOfThreeInput attempts to unpack [input] into the *big.Int type argument // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackPowOfThreeInput(input []byte) (*big.Int, error) { res, err := CalculatorplusABI.UnpackInput("powOfThree", input) if err != nil { return new(big.Int), err } unpacked := *abi.ConvertType(res[0], new(*big.Int)).(**big.Int) return unpacked, nil } // PackPowOfThree packs [base] of type *big.Int into the appropriate arguments for powOfThree. // the packed bytes include selector (first 4 func signature bytes). // This function is mostly used for tests. func PackPowOfThree(base *big.Int) ([]byte, error) { return CalculatorplusABI.Pack("powOfThree", base) } // PackPowOfThreeOutput attempts to pack given [outputStruct] of type PowOfThreeOutput // to conform the ABI outputs. func PackPowOfThreeOutput(outputStruct PowOfThreeOutput) ([]byte, error) { return CalculatorplusABI.PackOutput("powOfThree", outputStruct.SecondPow, outputStruct.ThirdPow, outputStruct.FourthPow, ) } // UnpackPowOfThreeOutput attempts to unpack [output] as PowOfThreeOutput // assumes that [output] does not include selector (omits first 4 func signature bytes) func UnpackPowOfThreeOutput(output []byte) (PowOfThreeOutput, error) { outputStruct := PowOfThreeOutput{} err := CalculatorplusABI.UnpackIntoInterface(&outputStruct, "powOfThree", output) return outputStruct, err } func powOfThree(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, PowOfThreeGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the PowOfThreeInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackPowOfThreeInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output PowOfThreeOutput // CUSTOM CODE FOR AN OUTPUT output.SecondPow = big.NewInt(0).Exp(inputStruct, big.NewInt(2), nil) output.ThirdPow = big.NewInt(0).Exp(inputStruct, big.NewInt(3), nil) output.FourthPow = big.NewInt(0).Exp(inputStruct, big.NewInt(4), nil) packedOutput, err := PackPowOfThreeOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // UnpackSimplFracInput attempts to unpack [input] as SimplFracInput // assumes that [input] does not include selector (omits first 4 func signature bytes) func UnpackSimplFracInput(input []byte) (SimplFracInput, error) { inputStruct := SimplFracInput{} err := CalculatorplusABI.UnpackInputIntoInterface(&inputStruct, "simplFrac", input) return inputStruct, err } // PackSimplFrac packs [inputStruct] of type SimplFracInput into the appropriate arguments for simplFrac. func PackSimplFrac(inputStruct SimplFracInput) ([]byte, error) { return CalculatorplusABI.Pack("simplFrac", inputStruct.Numerator, inputStruct.Denominator) } // PackSimplFracOutput attempts to pack given [outputStruct] of type SimplFracOutput // to conform the ABI outputs. func PackSimplFracOutput(outputStruct SimplFracOutput) ([]byte, error) { return CalculatorplusABI.PackOutput("simplFrac", outputStruct.SimplNum, outputStruct.SimplDenom, ) } // UnpackSimplFracOutput attempts to unpack [output] as SimplFracOutput // assumes that [output] does not include selector (omits first 4 func signature bytes) func UnpackSimplFracOutput(output []byte) (SimplFracOutput, error) { outputStruct := SimplFracOutput{} err := CalculatorplusABI.UnpackIntoInterface(&outputStruct, "simplFrac", output) return outputStruct, err } func simplFrac(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, SimplFracGasCost); err != nil { return nil, 0, err } // attempts to unpack [input] into the arguments to the SimplFracInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackSimplFracInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE _ = inputStruct // CUSTOM CODE OPERATES ON INPUT var output SimplFracOutput // CUSTOM CODE FOR AN OUTPUT // If denominator is 0, return both 0 if inputStruct.Denominator.Cmp(big.NewInt(0)) == 0 { output.SimplDenom = big.NewInt(0) output.SimplNum = big.NewInt(0) } else { // First, find common denominator var gcd big.Int gcd.GCD(nil, nil, inputStruct.Numerator, inputStruct.Denominator) // Now, simplify fraction output.SimplNum = big.NewInt(0).Div(inputStruct.Numerator, &gcd) output.SimplDenom = big.NewInt(0).Div(inputStruct.Denominator, &gcd) } packedOutput, err := PackSimplFracOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } // createCalculatorplusPrecompile returns a StatefulPrecompiledContract with getters and setters for the precompile. func createCalculatorplusPrecompile() contract.StatefulPrecompiledContract { var functions []*contract.StatefulPrecompileFunction abiFunctionMap := map[string]contract.RunStatefulPrecompileFunc{ "moduloPlus": moduloPlus, "powOfThree": powOfThree, "simplFrac": simplFrac, } for name, function := range abiFunctionMap { method, ok := CalculatorplusABI.Methods[name] if !ok { panic(fmt.Errorf("given method (%s) does not exist in the ABI", name)) } functions = append(functions, contract.NewStatefulPrecompileFunction(method.ID, function)) } // Construct the contract with no fallback function. statefulContract, err := contract.NewStatefulPrecompileContract(nil, functions) if err != nil { panic(err) } return statefulContract } ``` # Setting the ConfigKey & ContractAddress (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/05-set-configkey-contractaddr) --- title: Setting the ConfigKey & ContractAddress description: Learn how to set the ConfigKey, ContractAddress and register the Precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- ## ConfigKey Just as with the MD5 precompile in the previous section, go to the `module.go` file and set a `ConfigKey`. ## Contract Address In the same file, set a `ContractAddress`. Choose one that has not been used by other precompiles of earlier sections. ## Registration Go to the `plugin/main.go` file and register the new precompile. # Creating Genesis Block with precompileConfig (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/06-create-genesis-block) --- title: Creating Genesis Block with precompileConfig description: Learn how to create a genesis block with precompileConfig. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- In order to create a new Blockchain from our customized EVM, we have to define a Genesis block just as we did in out section earlier. To incorporate the CalculatorPlus precompile as part of the genesis block, we need to create a new genesis file and include a key for the CalculatorPlus precompile in the configuration JSON. This key, in particular, is the `configKey` that we specified in the `module.go` file of the Calculator precompile in the previous section. Copy over `Calculator.json` into a new file called `CalculatorPlus.json` and add the necessary key-pair value to the config. ```json title="CalculatorPlus.json" { "config": { "chainId": 99999, "homesteadBlock": 0, "eip150Block": 0, "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "istanbulBlock": 0, "muirGlacierBlock": 0, "subnetEVMTimestamp": 0, "feeConfig": { "gasLimit": 20000000, "minBaseFee": 1000000000, "targetGas": 100000000, "baseFeeChangeDenominator": 48, "minBlockGasCost": 0, "maxBlockGasCost": 10000000, "targetBlockRate": 2, "blockGasCostStep": 500000 }, "sha256Config": { "blockTimestamp": 0 }, "calculatorConfig": { "blockTimestamp": 0 }, "calculatorplusConfig" : { "blockTimestamp": 0 } }, "alloc": { "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { "balance": "0x52B7D2DCC80CD2E4000000" } }, "nonce": "0x0", "timestamp": "0x0", "extraData": "0x00", "gasLimit": "0x1312D00", "difficulty": "0x0", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000", "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } ``` # Testing Precompiles via Go (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/07-testing-precompile) --- title: Testing Precompiles via Go description: Learn how to test your Precompiles using Golang. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- > Program testing can be used to show the presence of bugs, but never to show their absence. - Edsger W. Dijkstra Let's once again examine the functions of the Calculator precompile and what each one does (in layman terms): - `add`: computes the sum of two numbers - `nextTwo`: returns the next two numbers that come after the input given - `repeat`: repeats a string N number of times While we spent a good amount of time in the previous sections examining Calculator Solidity interface and the Go logic, we forgot to do one thing: *test the functionality of Calculator*. But why test? For some, it may seems pointless to test such basic functions. However, testing all functions is important because it helps validate our belief that the logic of our functions is what we intended them to be. In this section, we will focus on utilizing three types of tests for our Calculator precompile: 1. Autogenerated Tests 2. Unit Tests 3. Fuzz Tests # Modify Autogenerated Tests (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/08-autogenerated-tests) --- title: Modify Autogenerated Tests description: Learn how to modify autogenerated tests in Go. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- The autogenerated tests will help us making sure, that our precompile throw an error in case not enough gas is supplied. To start, go to calculator/contract_test.go, where you will see the following: ```go // Code generated // This file is a generated precompile contract test with the skeleton of test functions. // The file is generated by a template. Please inspect every code and comment in this file before use. package calculator import ( "testing" "github.com/ava-labs/subnet-evm/core/state" "github.com/ava-labs/subnet-evm/precompile/testutils" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ) // These tests are run against the precompile contract directly with // the given input and expected output. They're just a guide to // help you write your own tests. These tests are for general cases like // allowlist, readOnly behaviour, and gas cost. You should write your own // tests for specific cases. var ( tests = map[string]testutils.PrecompileTest{ "insufficient gas for add should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // populate test input here testInput := AddInput{} input, err := PackAdd(testInput) require.NoError(t, err) return input }, SuppliedGas: AddGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas for nextTwo should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // set test input to a value here var testInput *big.Int input, err := PackNextTwo(testInput) require.NoError(t, err) return input }, SuppliedGas: NextTwoGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas for repeat should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // populate test input here testInput := RepeatInput{} input, err := PackRepeat(testInput) require.NoError(t, err) return input }, SuppliedGas: RepeatGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, } ) // TestCalculatorEmptyRun tests the Run function of the precompile contract. func TestCalculatorEmptyRun(t *testing.T) { // Run tests. for name, test := range tests { t.Run(name, func(t *testing.T) { test.Run(t, Module, state.NewTestStateDB(t)) }) } } func BenchmarkCalculatorEmpty(b *testing.B) { // Benchmark tests. for name, test := range tests { b.Run(name, func(b *testing.B) { test.Bench(b, Module, state.NewTestStateDB(b)) }) } } ``` There is a lot to digest in `contract_test.go`, but the file can be divided into the following three sections: - Unit Tests - TestCalculatorEmptyRun - BenchmarkCalculator The autogenerated unit tests are stored in the variable var, which is a mapping from strings (the description of the tests cases) to individual unit tests (`testutils.PrecompileTest`). Upon inspecting the keys of each pair, you will see that all three autogenerated unit tests are checking the same thing that: `add`, `nextTwo`, and `repeat` fail if not enough gas is provided. Each test expects to fail when supplying too little gas: ```go { // ... SuppliedGas: RepeatGasCost - 1, ExpectedErr: vmerrs.ErrOutOfGas.Error(), } ``` As of currently, if you attempt to build and run the test cases, you will not get very far because there is one step we need to do to: pass in arguments for each autogenerated unit test. For each variable testInput defined in each unit test, add an argument. What argument to put down does not matter, it just needs to be a valid argument. An example of what arguments to put in can be found below: ```go var ( tests = map[string]testutils.PrecompileTest{ "insufficient gas for add should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // populate test input here testInput := AddInput{big.NewInt(1), big.NewInt(1)} input, err := PackAdd(testInput) require.NoError(t, err) return input }, SuppliedGas: AddGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas for nextTwo should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // set test input to a value here // var testInput *big.Int testInput := big.NewInt(1) input, err := PackNextTwo(testInput) require.NoError(t, err) return input }, SuppliedGas: NextTwoGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas for repeat should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // populate test input here testInput := RepeatInput{big.NewInt(1), "EGS"} input, err := PackRepeat(testInput) require.NoError(t, err) return input }, SuppliedGas: RepeatGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, } ) ``` Now run the test by running the following command from the project root: ```bash ./scripts/build_test.sh ``` # Adding Unit Tests (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/09-unit-tests) --- title: Adding Unit Tests description: Learn how to add unit tests. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Although the autogenerated units tests were useful in finding whether if the gas requirements of our functions were being fulfilled, we are yet to test the actual logic of our functions. We can add more unit tests by simply adding more mappings to the tests variable. To start, lets define the tests that we want to write for each function within our Calculator precompile: - `add`: check that **add(1, 2)** returns 3 - `nextTwo`: check that **nextTwo(1)** returns 2, 3 - `repeat`: check that **repeat(2, "EGS")** returns "EGSEGS" With this in mind, lets add the three units tests to the tests variable! Below is a code excerpt which shows the three unit tests incorporated: ```go var ( expectedNextTwoOutcome, _ = PackNextTwoOutput(NextTwoOutput{big.NewInt(2), big.NewInt(3)}) expectedRepeatOutcome, _ = PackRepeatOutput("EGSEGS") expectedAddOutcome = common.LeftPadBytes(big.NewInt(3).Bytes(), common.HashLength) tests = map[string]testutils.PrecompileTest{ "insufficient gas for add should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // populate test input here testInput := AddInput{big.NewInt(1), big.NewInt(1)} input, err := PackAdd(testInput) require.NoError(t, err) return input }, SuppliedGas: AddGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas for nextTwo should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // set test input to a value here // var testInput *big.Int testInput := big.NewInt(1) input, err := PackNextTwo(testInput) require.NoError(t, err) return input }, SuppliedGas: NextTwoGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, "insufficient gas for repeat should fail": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { // CUSTOM CODE STARTS HERE // populate test input here testInput := RepeatInput{big.NewInt(1), "EGS"} input, err := PackRepeat(testInput) require.NoError(t, err) return input }, SuppliedGas: RepeatGasCost - 1, ReadOnly: false, ExpectedErr: vmerrs.ErrOutOfGas.Error(), }, "testing add": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { value1 := big.NewInt(1) value2 := big.NewInt(2) testInput := AddInput{value1, value2} input, err := PackAdd(testInput) require.NoError(t, err) return input }, SuppliedGas: AddGasCost, ReadOnly: true, ExpectedRes: expectedAddOutcome, }, "testing nextTwo": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { testInput := big.NewInt(1) input, err := PackNextTwo(testInput) require.NoError(t, err) return input }, SuppliedGas: NextTwoGasCost, ReadOnly: true, ExpectedRes: expectedNextTwoOutcome, }, "testing repeat": { Caller: common.Address{1}, InputFn: func(t testing.TB) []byte { baseString := "EGS" timesToRepeat := big.NewInt(2) input, err := PackRepeat(RepeatInput{timesToRepeat, baseString}) require.NoError(t, err) return input }, SuppliedGas: RepeatGasCost, ReadOnly: true, ExpectedRes: expectedRepeatOutcome, }, } ) ``` # Adding Fuzz Tests (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/10-fuzz-tests) --- title: Adding Fuzz Tests description: Learn how to add Fuzz tests. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Units tests are useful when comparing the outcome of a predetermined input with the expected outcome. If we wanted to check that our function works as expected for all given inputs, the only way to show this would be to test every possible input. However, this is not possible for types such as uint256, as it would require us to test for 2^256 possible inputs. Even if we tested 1 input per second (or 2, 3, 4, 1000, etc.), this process would outlast the universe itself. Rather than testing every possible output, we can strengthen our confidence of the expected behavior of a function by testing a random sample of inputs. By testing randomly, we can test for inputs that we wouldn't have originally thought of and are able to see how our function behaves over a range of values. However, given that generating random inputs is not deterministic, we cannot use the tests variable itself. Rather, we will need to leverage the `TestCalculatorRun` function: ```go // TestCalculatorEmptyRun tests the Run function of the precompile contract. func TestCalculatorRun(t *testing.T) { // Run tests. for name, test := range tests { t.Run(name, func(t *testing.T) { test.Run(t, Module, state.NewTestStateDB(t)) }) } } ``` The `TestCalculatorRun` function, by default, iterates through each unit test defined in the tests variable on runs said tests. However, we are not limited to just using `TestCalculatorRun` for the unit tests in tests. We can expand the functionality of `TestCalculatorRun` by adding our fuzz tests. In particular, we can define the following logic for `TestCalculatorRun`: - Iterate N number of times - For each iteration, pick two numbers in the range from 0 to n - After picking two random numbers, create a unit test with the two random numbers as inputs and execute said unit test With this logic in mind, below is the code for how we would go about implementing fuzzing in `TestCalculatorRun`: ```go // TestCalculatorRun tests the Run function of the precompile contract. func TestCalculatorRun(t *testing.T) { // Run tests. for name, test := range tests { t.Run(name, func(t *testing.T) { test.Run(t, Module, state.NewTestStateDB(t)) }) } // Defining own test cases here N := 1_000 n := new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(128)), nil) // Fuzzing N times for i := 0; i < N; i++ { // Adding randomization test here randomInt1, err := rand.Int(rand.Reader, n) randomInt2, err := rand.Int(rand.Reader, n) // Expected outcome expectedRandOutcome := common.LeftPadBytes(big.NewInt(0).Add(randomInt1, randomInt2).Bytes(), common.HashLength) // Pack add input randTestInput := AddInput{randomInt1, randomInt2} randInput, err := PackAdd(randTestInput) require.NoError(t, err) randTest := testutils.PrecompileTest{ Caller: common.Address{1}, Input: randInput, SuppliedGas: AddGasCost, ReadOnly: true, ExpectedRes: expectedRandOutcome, } t.Run("Testing random sum!", func(t *testing.T) { randTest.Run(t, Module, state.NewTestStateDB(t)) }) } } ``` # Testing CalculatorPlus (/academy/avalanche-l1/customizing-evm/08-calculator-precompile/11-test-calculatorplus) --- title: Testing CalculatorPlus description: Learn how to test CalculatorPlus precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Callout } from 'fumadocs-ui/components/callout'; Now that we have gone through an example of how to test the Calculator precompile, it is time that you develop your own tests for CalculatorPlus. Rather than explicitly tell you what test cases to write, we decided to leave that to you. After all, testing is a subjective profession - a test suite that fits one security needs might be inadequate for someone else. However, if you want an idea for what to test for, the following are some recommendations: - Unit Tests: create some unit tests involving values that you can come up with. Ideally, write 3-5 unit tests for each function. - Fuzz Tests: test more than 1000 random inputs for each function For both unit and fuzz tests, make sure to account for the case when the denominator is equal to 0 in `simplFrac`. # Creating Counter Precompile (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/00-intro) --- title: Creating Counter Precompile description: Learn how to create a Stateful Counter Precompiles in Avalanche L1 EVM. updated: 2024-05-31 authors: [ashucoder9] icon: Book --- ## What We Are Building It's time to build our first stateful precompile. In particular, we'll build a counter that keeps track of an integer. Our Counter precompile will have the following logic: - Users are able to get the current value of the counter - Users are able to set a new value to the counter - Users are able to increment the value of the counter To help you understand, we've provided a reference stateful precompile: **StringStore**. As the name suggests, StringStore stores a string that users can change. This string is stored in the EVM state. ## Overview of Steps Compared to the process before, we must also add tests for our precompile. Here's a quick overview of the steps we'll follow: 1. Create a Solidity interface for the precompile and generate the ABI 2. Generate the precompile Go boilerplate files 3. Write the precompile code in Go with access to the EVM state 4. Set the initial state in the configurator and register the precompile 5. Add and run tests 6. Build and run your customized EVM 7. Connect Remix to your customized EVM and interact with the counter This tutorial will help you create more complex precompiles. Let's begin! # Create Solidity Interface (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/01-create-solidity-interface) --- title: Create Solidity Interface description: Learn how to create a solidity interface for your counter precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Now, we'll create a Solidity interface for our Stateful Precompile. ## StringStore Solidity Interface To start, let's look at the interface of the **StringStore** precompile: ```solidity title="contracts/contracts/interfaces/IStringStore.sol" // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IStringStore { function getString() external view returns (string memory value); function setString(string memory value) external; } ``` As seen above, we have the following two functions defined: 1. `getString`: retreives the string from the stateful precompile 2. `setString`: sets a string in the stateful precompile ## Create Solidity Interface for Counter Create an interface for the counter in the same directory called `ICounter.sol`. Your interface should have the following three functions declared: 1. `getCounter`: returns the counter value from the stateful precompile 2. `incrementCounter`: when called, this function increments the current counter 3. `setCounter`: takes in a value, and sets it as the counter of the stateful precompile For any argument/return value, make sure it is named as `value`. ```solidity // SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ICounter { function getCounter() external view returns (uint value); function incrementCounter() external; function setCounter(uint value) external; } ``` ## Generate the ABI Now that we have an interface of our precompile, let's create an ABI of our Solidity interface. Open the terminal (control + \`), change to the `/contracts` directory, and run the command to compile the solidity interface to ABI: ```bash # Move to contracts directory cd contracts # Compile ICounter.sol to ABI npx solc@latest --abi ./contracts/interfaces/ICounter.sol -o ./abis --base-path . --include-path ./node_modules # Rename mv ./abis/contracts_interfaces_ICounter_sol_ICounter.abi ./abis/ICounter.abi ``` # Store Data in EVM State (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/02-store-data-in-evm) --- title: Store Data in EVM State description: Learn how to store data in the EVM state. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Like all stateful machines, the EVM provides us with a way to save data. In particular, the EVM exposes a key-value mapping we can leverage to create stateful precompiles. The specifics of this mapping are as follows: - Key: a tuple consisting of an address and the storage key of the type Hash - Value: any data encoded in a Hash, also called a word in the EVM ## Storage Slots Each storage slot is uniquely identified by the combination of an address and a storage key. To keep things organized, smart contracts and precompiles use their own address and a storage key to store data related to them. Look at the reference implementation `StringStore`. The storage key is defined in the second variable group in `contract.go`: ```go // Singleton StatefulPrecompiledContract and signatures. var ( // StringStoreRawABI contains the raw ABI of StringStore contract. //go:embed contract.abi StringStoreRawABI string StringStoreABI = contract.ParseABI(StringStoreRawABI) StringStorePrecompile = createStringStorePrecompile() // Key that defines where our string will be stored storageKeyHash = common.BytesToHash([]byte("storageKey")) ) ``` We can use any string as our storage key. Then we convert it to a byte array and convert it to the hex representation. > The common.BytesToHash is not hashing the string, but converting it to a 32 byte array in hex representation. If we store multiple variables in the EVM state, we will define multiple keys here. Since we only use a single storage slot in this example, we can just call it `storageKey`. At this point, we are not restricted in what state we can access from the precompile. We have access to the entire `stateDB` and can modify the entire EVM state including data other precompiles saved to state or balances of accounts. **This is very powerful, but also potentially dangerous**. ## Converting the Value to Hash Type Since the StateDB only stores data of the type *Hash*, we need to convert all values to the type *Hash* before passing it to the **stateDB**. ### Converting Numbers To convert numbers of type `big.Int`, we can utilize a function from the common package: ```go valueHash := common.BigToHash(value) ``` Since the `big.Int` data structure already is 32-bytes long, the conversion is straightforward. ```go // BigToHash sets byte representation of b to hash. // If b is larger than len(h), b will be cropped from the left. func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } ``` ### Converting Strings Converting strings is more challenging, since they are variable in length. Let's see an example: ```go input := "Hello World" ``` To start, let's convert input into type bytes: ```go inputAsBytes := []byte(input) // [72 101 108 108 111 32 87 111 114 108 100] ``` This is how you would convert input to type bytes in Go. Notice that the comment in the code snippet is the byte-representation of input, where each integer represents a byte. Right now, inputAsBytes is of length 11. We want it to be of length 32. Therefore, we pad `inputAsBytes`, adding however many zeros to the front until `inputAsBytes` has 32 integers: ```go inputPadded := common.LeftPadBytes(inputAsBytes, common.HashLength) // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 72 101 108 108 111 32 87 111 114 108 100] ``` In Go, we would do that using the function common.LeftPadBytes, which takes the byte array to be padded and the desired length. The desired length is supplied with the common.HashLength variable, which has the value 32. As seen in the comment, inputPadded is now of length 32. In the next step, we convert the bytes to a Hash with the function `common.BytesToHash`: ```go inputHash := common.BytesToHash(inputPadded) // 0x00000000000000000000000000000000000000000048656c6c6f20576f726c64 ``` ## Helper Functions To make the code reusable and keep the precompile function clean, it makes sense to create helper functions for converting and storing the data. The first one we'll see is StoreString: ```go // StoreString sets the value of the storage key "storageKey" in the contract storage. func StoreString(stateDB contract.StateDB, newValue string) { newValuePadded := common.LeftPadBytes([]byte(newValue), common.HashLength) newValueHash := common.BytesToHash(newValuePadded) stateDB.SetState(ContractAddress, storageKeyHash, newValueHash) } ``` `StoreString` takes in the underlying key-value mapping, known as **StateDB**, and the new value, and updates the current string stored to be the new one. `StoreString` takes care of any type conversions and state management for us. Focusing now on `setString`, defining the logic is relatively easy. All we need to do is pass in **StateDB** and our string to `StoreString`. Thus, we have the following: ```go func setString(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, SetStringGasCost); err != nil { return nil, 0, err } if readOnly { return nil, remainingGas, vmerrs.ErrWriteProtection } // attempts to unpack [input] into the arguments to the SetStringInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackSetStringInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE // Get K-V Mapping currentState := accessibleState.GetStateDB() // Set the value StoreString(currentState, inputStruct) // this function does not return an output, leave this one as is packedOutput := []byte{} // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` # Implementing setCounter (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/03-implement-set-counter) --- title: Implementing setCounter description: Learn how to implement the setCounter method. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Having seen how strings are stored in `StringStore`, its time for us to store integers with Counter. The thought process for this section can be defined as follows: - Define the storage hash for our counter - Implement `StoreCounterValue`, a helper function which acts like `StoreString` in the previous - Implement `setCounter` ```go title="contract.go" // storageKeyHash storageKeyHash = common.BytesToHash([]byte("counterValue")) // StoreGreeting sets the value of the storage key in the contract storage. func StoreCounterValue(stateDB contract.StateDB, value *big.Int) { // Convert uint to left padded bytes inputPadded := common.LeftPadBytes(value.Bytes(), 32) inputHash := common.BytesToHash(inputPadded) stateDB.SetState(ContractAddress, storageKeyHash, inputHash) } //setCounter sets the counter value in the contract storage. func setCounter(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, SetCounterGasCost); err != nil { return nil, 0, err } if readOnly { return nil, remainingGas, vmerrs.ErrWriteProtection } // attempts to unpack [input] into the arguments to the SetCounterInput. // Assumes that [input] does not include selector // You can use unpacked [inputStruct] variable in your code inputStruct, err := UnpackSetCounterInput(input) if err != nil { return nil, remainingGas, err } // CUSTOM CODE STARTS HERE // Get the current state currentState := accessibleState.GetStateDB() // Set the value StoreCounterValue(currentState, inputStruct) // this function does not return an output, leave this one as is packedOutput := []byte{} // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` # Read Data From EVM State (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/04-read-date-from-evm) --- title: Read Data From EVM State description: Learn how to read the data from EVM state. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- In the section about storing data in the EVM, we learned about how to store our string in the EVM State. An equally important skill is how to read data from the EVM state. In this section, we'll learn about how to retrieve our string from the EVM state. ## Defining Helper Function Just like with setting the string in the EVM state, there are some conversions we'll have to perform to our string. In particular, we'll need to unhash the value stored in StateDB to get our original string. Thus, our helper function can be defined as follows: ```go // GetString returns the value of the storage key "storageKey" in the contract storage, // with leading zeroes trimmed. func GetString(stateDB contract.StateDB) string { // Get the value set at recipient value := stateDB.GetState(ContractAddress, storageKeyHash) return string(common.TrimLeftZeroes(value.Bytes())) } ``` With our helper function defined, we can implement the logic for `getString`. This will consists of retrieving the underlying key-value mapping and then passing it to `GetString`. ```go func getString(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, GetStringGasCost); err != nil { return nil, 0, err } // no input provided for this function // CUSTOM CODE STARTS HERE var output string // CUSTOM CODE FOR AN OUTPUT currentState := accessibleState.GetStateDB() output = GetString(currentState) packedOutput, err := PackGetStringOutput(output) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` # Implementing getCounter & increment (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/05-implement-getcounter-increment) --- title: Implementing getCounter & increment description: Learn how to implement getCounter and increment. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Having seen how to retrieve strings from the EVM state with the StoreString precompile, we are now ready to implement `getCounter`. Also, now that we are familiar with reading and writing to the EVM state, we can implement increment, which requires both read and write operations. ## Implementing getCounter For `getCounter`, the following thought process is helpful: - Create a helper function `GetCounterValue`, which takes in the current StateDB and returns the integer stored at the `storageKeyHash` - In `getCounter`, get the current StateDB and pass it to `GetCounterValue` ```go // GetCounterValue gets the value of the storage key in the contract storage. func GetCounterValue(stateDB contract.StateDB) *big.Int { // Get the value value := stateDB.GetState(ContractAddress, storageKeyHash) // Convert bytes to uint return new(big.Int).SetBytes(value.Bytes()) } func getCounter(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, GetCounterGasCost); err != nil { return nil, 0, err } // no input provided for this function // Get the current state currentState := accessibleState.GetStateDB() // Get the value set at recipient value := GetCounterValue(currentState) packedOutput, err := PackGetCounterOutput(value) if err != nil { return nil, remainingGas, err } // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` ## Implementing increment For increment, the following thought process is helpful: - Get the current StateDB and pass it to GetCounterValue - Once you have the current counter, increment it by one - Store the new counter with StoreCounterValue ```go func incrementCounter(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { if remainingGas, err = contract.DeductGas(suppliedGas, IncrementCounterGasCost); err != nil { return nil, 0, err } if readOnly { return nil, remainingGas, vmerrs.ErrWriteProtection } // no input provided for this function // CUSTOM CODE STARTS HERE // Get the current state currentState := accessibleState.GetStateDB() // Get the value of the counter value := GetCounterValue(currentState) // Set the value StoreCounterValue(currentState, value.Add(value, big.NewInt(1))) // this function does not return an output, leave this one as is packedOutput := []byte{} // Return the packed output and the remaining gas return packedOutput, remainingGas, nil } ``` # Setting Base Gas Fees of Your Precompile (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/06-setting-base-gasfees) --- title: Setting Base Gas Fees of Your Precompile description: Learn how to set the base gas fees of your precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- Gas is used for spam prevention. If our precompile is accessible to everyone on our Avalanche L1, it is important to set the gas cost in a way that lets users economically utilize it in their apps, yet prevents spammers from spamming our blockchain with calls to the precompile. In this section, we'll modify the default values of the gas costs of our Calculator precompile. By default, all user-defined functions have a gas cost of 1. While this is good news for the average user, allowing users to call computationally expensive functions can leave our blockchain vulnerable to Denial-of-Service (DoS) attacks. We can find the default gas cost values for our functions in calculator/contract.go. Following the import statements, you should see: ```go title-"calculator/contract.go" const ( // Gas costs for each function. These are set to 1 by default. // You should set a gas cost for each function in your contract. // Generally, you should not set gas costs very low as this may cause your network to be vulnerable to DoS attacks. // There are some predefined gas costs in contract/utils.go that you can use. AddGasCost uint64 = 1 /* SET A GAS COST HERE */ NextTwoGasCost uint64 = 1 /* SET A GAS COST HERE */ RepeatGasCost uint64 = 1 /* SET A GAS COST HERE */ ) ``` The variables `AddGasCost`, `NextTwoGasCost`, and `RepeatGasCost` are the gas costs of the `add`, `nextTwo`, and `repeat` functions, respectively. As you can see, they are currently set to 1. However, changing the gas cost is as easy as changing the values themselves. As an example, change the gas costs to 7. # Setting ConfigKey and ContractAddress (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/07-set-configkey-contractaddr) --- title: Setting ConfigKey and ContractAddress description: Learn how to set the ConfigKey and ContractAddress, and Register the Precompile. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- ## ConfigKey Just as with the MD5 precompile in the previous section, go to the `module.go` file and set a ConfigKey. ## Contract Address In the same file, set a ContractAddress. Choose one that has not been used by other precompiles of earlier sections. # Initial State (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/08-initial-state) --- title: Initial State description: Setting the Initial State. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Throughout this chapter, we've covered storing variables in the EVM state. However, all of this was through the use of StringStore/Counter Solidity interface. An important part of stateful precompiles is the ability to initialize values stored by our precompiled contracts. In the next few chapters, we'll see how to initialize the values of our precompiled contracts by manually setting the values or allowing for the values to be defined in the `genesis.json` file. # Defining Default Values via Golang (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/09-define-default-values-via-go) --- title: Defining Default Values via Golang description: Learn how to set the default values of precompiled contracts using Go. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- In this section, we'll cover defining default values of precompiled contracts using Go. We'll refer to StringStore for this section. ## Configure Function To start, go to `StringStore/module.go` and scroll to the end of the file. There, you will find the Configure function: ```go // Configure configures [state] with the given [cfg] precompileconfig. // This function is called by the EVM once per precompile contract activation. // You can use this function to set up your precompile contract's initial state, // by using the [cfg] config and [state] stateDB. func (*configurator) Configure(chainConfig precompileconfig.ChainConfig, cfg precompileconfig.Config, state contract.StateDB, blockContext contract.ConfigurationBlockContext) error { config, ok := cfg.(*Config) if !ok { return fmt.Errorf("incorrect config %T: %v", config, config) } // CUSTOM CODE STARTS HERE return nil } ``` Configure handles the initialization of a precompiled contract. We want to use Configure to define the default value for the string we are storing. But how? Configure gives us access to the StateDB (as one of the function parameters) and also lets us call any functions defined in `contract.go`. For example, if we wanted the default string to be "EGS," then we would just have to write one line of code: ```go // Configure configures [state] with the given [cfg] precompileconfig. // This function is called by the EVM once per precompile contract activation. // You can use this function to set up your precompile contract's initial state, // by using the [cfg] config and [state] stateDB. func (*configurator) Configure(chainConfig precompileconfig.ChainConfig, cfg precompileconfig.Config, state contract.StateDB, blockContext contract.ConfigurationBlockContext) error { config, ok := cfg.(*Config) if !ok { return fmt.Errorf("incorrect config %T: %v", config, config) } // CUSTOM CODE STARTS HERE StoreString(state, "EGS") return nil } ``` We have just set a default value for our precompiled contract. # Defining Default Values via Genesis (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/10-define-default-values-via-genesis) --- title: Defining Default Values via Genesis description: Learn how to set the default values of precompiled contracts using genesis JSON file. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- In the last section, we saw how to initialize the default values of our precompiled contracts via the Configure function. While straightforward, it would be ideal for us (and developers who don't write in Go) to initialize default values via the genesis JSON. Here's how to do just that. ## Modifying config.go The first step is to extract the value from the JSON file. Go to `config.go` and look at the Config struct: ```go // Config implements the precompileconfig.Config interface and // adds specific configuration for StringStore. type Config struct { precompileconfig.Upgrade // CUSTOM CODE STARTS HERE // Add your own custom fields for Config here } ``` Within the Config struct, we can find another struct: the Upgrade struct. It is not necessary to look at the definition of this struct. However, it is useful to know that this struct allows for the `blockTimestamp` key to be parsed in our `genesis.json` file. ```go "stringStoreConfig" : { "blockTimestamp": 0, } ``` To pass in default values via the genesis JSON, we must complete the following: - Define a key-value in the genesis JSON - Update our Config struct so it is able to parse our new key-value In the case of StringStore, after defining blockTimestamp, we will add another key-pair for our default string: ```go "stringStoreConfig" : { "blockTimestamp": 0, "defaultString": "EGS" } ``` Next, we will want to update our Config struct: ```go type Config struct { precompileconfig.Upgrade // CUSTOM CODE STARTS HERE // Add your own custom fields for Config here DefaultString string `json:"defaultString,omitempty"` } ``` In the above snippet, we are defining a new field of type string named `DefaultString`. With respect to its initialization, the value of `DefaultString` is derived from the JSON key `defaultString` from our genesis JSON (the value `json:"defaultString,omitempty"` tells Go to ignore the JSON field if we do not define it in our genesis JSON). At this point, we have defined our new key-value pair in the genesis JSON and incorporated the logic so that precompile-evm can parse the new key-value. However, one step remains. Although precompile-evm can parse the new key-value, we are not actually utilizing it anywhere. Our last-step is to update `Configure` method in `module.go` so it can read our new key-value pair. ```go func (*configurator) Configure(chainConfig contract.ChainConfig, cfg precompileconfig.Config, state contract.StateDB, _ contract.BlockContext) error { config, ok := cfg.(*Config) if !ok { return fmt.Errorf("incorrect config %T: %v", config, config) } // CUSTOM CODE STARTS HERE StoreString(state, config.DefaultString) return nil } ``` # Testing Your Precompile (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/11-testing-precompile-hardhat) --- title: Testing Your Precompile description: Learn how to test your precompile using Hardhat. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Just like with the Calculator precompile, we want to test the functionality of our precompile to make sure it behaves as intended. Although we can write tests in Go, this is not the only way we can test our precompile. We can leverage Hardhat, a popular smart contract testing framework, for testing. In this section, we'll cover writing the smart contract component of our tests and setting up our testing environment so that precompile-evm can execute these tests for us. ## Structure of Testing with Hardhat For `StringStore`, we'll need to complete the following tasks in order to leverage Hardhat: 1. Define the Genesis JSON file we want to use for testing 2. Tell precompile-evm what Hardhat script to use to test `StringStore` 3. Write the smart contract that will hold the test cases for us 4. Write the actual script that will execute the Hardhat tests for us ## Defining the Genesis JSON The easiest part of this tutorial, we will need to define the genesis state of our testing environment. For all intents and purposes, you can copy the genesis JSON that you used in the Defining Default Values section and paste it in `precompile-evm/tests/precompile/genesis`, naming it `StringStore.json` (it is important that you name the genesis file the name of your precompile). ## Telling Precompile-EVM What To Test The next step is to modify `suites.go` and update the file so that precompile-evm can call the Hardhat tests for StringStore. Go to `precompile-evm/tests/precompile/solidity` and find `suites.go`. Currently, your file should look like the following: ```go title="suites.go" // Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // Implements solidity tests. package solidity import ( "context" "time" "github.com/ava-labs/subnet-evm/tests/utils" ginkgo "github.com/onsi/ginkgo/v2" ) var _ = ginkgo.Describe("[Precompiles]", ginkgo.Ordered, func() { utils.RegisterPingTest() // Each ginkgo It node specifies the name of the genesis file (in ./tests/precompile/genesis/) // to use to launch the subnet and the name of the TS test file to run on the subnet (in ./contract-examples/tests/) // ADD YOUR PRECOMPILE HERE /* ginkgo.It("your precompile", ginkgo.Label("Precompile"), ginkgo.Label("YourPrecompile"), func() { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() ​ // Specify the name shared by the genesis file in ./tests/precompile/genesis/{your_precompile}.json // and the test file in ./contracts/tests/{your_precompile}.ts // If you want to use a different test command and genesis path than the defaults, you can // use the utils.RunTestCMD. See utils.RunDefaultHardhatTests for an example. utils.RunDefaultHardhatTests(ctx, "your_precompile") }) */ }) ``` If you view the comments generated, you will already see a general structure for how we can declare our tests for precompiles. Let's take this template and use it to declare the tests for StringStore! ```go title="suites.go" // Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. // Implements solidity tests. package solidity import ( "context" "time" "github.com/ava-labs/subnet-evm/tests/utils" ginkgo "github.com/onsi/ginkgo/v2" ) var _ = ginkgo.Describe("[Precompiles]", ginkgo.Ordered, func() { utils.RegisterPingTest() // Each ginkgo It node specifies the name of the genesis file (in ./tests/precompile/genesis/) // to use to launch the subnet and the name of the TS test file to run on the subnet (in ./contract-examples/tests/) // ADD YOUR PRECOMPILE HERE /* ginkgo.It("your precompile", ginkgo.Label("Precompile"), ginkgo.Label("YourPrecompile"), func() { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() ​ // Specify the name shared by the genesis file in ./tests/precompile/genesis/{your_precompile}.json // and the test file in ./contracts/tests/{your_precompile}.ts // If you want to use a different test command and genesis path than the defaults, you can // use the utils.RunTestCMD. See utils.RunDefaultHardhatTests for an example. utils.RunDefaultHardhatTests(ctx, "your_precompile") }) */ ginkgo.It("StringStore", ginkgo.Label("Precompile"), ginkgo.Label("StringStore"), func() { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() utils.RunDefaultHardhatTests(ctx, "StringStore") }) }) ``` Again, the naming conventions are important. We recommend you use the same name as your precompile whenever possible. ## Defining Test Contract In contrast to using just Go, Hardhat allows us to test our precompiles using Solidity. To start, create a new file called `StringStoreTest.sol` in `precompile-evm/contract/contracts`. We can start defining our file by including the following: ```solidity title="StringStoreTest.sol" // SPDX-License-Identifier: MIT pragma solidity >= 0.8.0; import "ds-test/src/test.sol"; import {IStringStore} from "../contracts/interfaces/IStringStore.sol"; ``` As of right now, there are two important things to note. We are first importing `test.sol`, a testing file that includes a range of assertion functions to assert that our outputs are as expected. In particular, these assertion functions are methods of the DSTest contract. Next, we are importing the interface of the StringStore precompile that we want to test. With this in mind, let's fill in the rest of our test file: ```solidity title="StringStoreTest.sol" contract StringStoreTest is DSTest { IStringStore stringStore = IStringStore(0x0300000000000000000000000000000000000005); function step_getString() public { assertEq(stringStore.getString(), "Cornell"); } function step_getSet() public { string memory newStr = "Apple"; stringStore.setString(newStr); assertEq(stringStore.getString(), newStr); } } ``` In the contract `StringStoreTest`, we are inheriting the DSTest contract so that we can leverage the provided assertion functions. Afterward, we are declaring the variable stringStore so that we can directly call the StringStore precompile. Next, we have the two testing functions. Briefly looking at the logic of each test function: - `step_getString`: We are testing that the getString function returns the default string defined in the genesis JSON (in this example, the default string is set to "Cornell") - `step_getSet`: We are assigning a new string to our precompile and making sure that setString does so correctly We now want to note the following two details regarding Solidity test functions in Hardhat: - Any test functions that you want to be called must start with the prefix "step" - The assertion function you use to check your outputs is `assertEq` With our Solidity test contract defined, let's write actual Hardhat script! ## Writing Your Hardhat Script To start, go to `precompile-evm/contracts/test` and create a new file called `StringStore.ts`. It is important to name your TypeScript file the same name as your precompile. Here are the steps to take when defining our Hardhat script: 1. Specify the address at which our precompile is located 2. Deploy our testing contract so that we can call the test functions 3. Tell Hardhat to execute said test functions To make our lives even easier, Avalanche L1-EVM (a library that Precompile-EVM leverages) has helper functions to call our test functions simply by specifying the names of the test functions. You can find the helper functions here: https://github.com/ava-labs/subnet-evm/blob/master/contracts/test/utils.ts. In the end, our testing file will look as follows: ```ts title="StringStore.ts" // (c) 2019-2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. import { ethers } from "hardhat" import { test } from "@avalabs/subnet-evm-contracts" import { factory } from "typescript" const STRINGSTORE_ADDRESS = "0x0300000000000000000000000000000000000005" describe("StringStoreTest", function() { this.timeout("30s") beforeEach("Setup DS-Test", async function () { const stringStorePromise = ethers.getContractAt("IStringStore", STRINGSTORE_ADDRESS) return ethers.getContractFactory("StringStoreTest").then(factory => factory.deploy()) .then(contract => { this.testContract = contract return contract.deployed().then(() => contract) }) }) test("Testing get function", "step_getString") test("Testing get and set function", "step_getSet") }) ``` ## Running Your Hardhat Test To run your HardHat tests, first change to the root directory of precompile-evm and run the following command: ```bash ./scripts/build.sh ``` Afterward, run the following command: ```bash GINKGO_LABEL_FILTER=StringStore ./scripts/run_ginkgo.sh ``` The variable `GINKGO_LABEL_FILTER` simply tells precompile-evm which tests suite from `suites.go` to execute. Execute the `StringStore` test suite. However, if you have multiple precompiles to test, set `GINKGO_LABEL_FILTER` equal to "Precompile." You should see something like the following: ```bash title="Combined output" StringStoreTest ✓ Testing get function (4126ms) ✓ Testing get and set function (4070ms) 2 passing (12s) < Exit [It] StringStore - /Users/Rodrigo.Villar/go/src/github.com/ava-labs/precompile-evm/tests/precompile/solidity/suites.go:40 @ 07/19/23 15:37:03.574 (16.134s) • [16.134 seconds] ------------------------------ [AfterSuite] /Users/Rodrigo.Villar/go/pkg/mod/github.com/ava-labs/subnet-evm@v0.5.2/tests/utils/command.go:85 > Enter [AfterSuite] TOP-LEVEL - /Users/Rodrigo.Villar/go/pkg/mod/github.com/ava-labs/subnet-evm@v0.5.2/tests/utils/command.go:85 @ 07/19/23 15:37:03.575 < Exit [AfterSuite] TOP-LEVEL - /Users/Rodrigo.Villar/go/pkg/mod/github.com/ava-labs/subnet-evm@v0.5.2/tests/utils/command.go:85 @ 07/19/23 15:37:03.575 (0s) [AfterSuite] PASSED [0.000 seconds] ------------------------------ Ran 2 of 3 Specs in 50.822 seconds SUCCESS! -- 2 Passed | 0 Failed | 0 Pending | 1 Skipped PASS ``` Congrats, you can now write Hardhat tests for your stateful precompiles! # Build Your Precompile (/academy/avalanche-l1/customizing-evm/10-stateful-counter-precompile/12-build-your-precompile) --- title: Build Your Precompile description: Learn how to build and run your precompile as a custom EVM blockchain. updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- ## Build Your Custom VM There's a simple build script in the Precompile-EVM we can utilize to build. First, make sure you are in the root folder of you Precompile-EVM: ```bash cd $GOPATH/src/github.com/ava-labs/precompile-evm ``` Then run the command to initiate the build script: ```bash ./scripts/build.sh ``` If you do not see any error, the build was successful. ## Run a Local Network with Your Custom VM You can run you customized Precompile-EVM by using the Avalanche CLI. First, create the configuration for your blockchain with custom VM. ```bash avalanche blockchain create myblockchain --custom --vm $AVALANCHEGO_PLUGIN_PATH/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy --genesis ./.devcontainer/genesis-example.json ``` Make sure you replace the binary name with the actual binary name of your Precompile-EVM, and genesis file with the actual path to your genesis file. Next, launch the Avalanche L1 with your custom VM: ```bash avalanche blockchain deploy myblockchain ``` After around 1 minute the blockchain should have been created and some more output should appear in the terminal. You'll also see the RPC URL of your blockchain in the terminal. # Token Bridging (/academy/avalanche-l1/erc20-bridge/01-token-bridging/01-token-bridging) --- title: Token Bridging description: Learn about asset transfer between chains updated: 2024-05-31 authors: [ashucoder9] icon: Book --- Asset bridging is a crucial concept in blockchain interoperability, enabling assets to be transferred across different blockchain networks. This process is essential for creating seamless experiences in multi-chain ecosystems. ## What You Will Learn In this section, you will go through the following topics: - **Bridging assets:** you will understand why bridging assets is important. - **Bridge safety:** learn about the most common hacks in bridges and how to prevent them. # Bridge Architecture (/academy/avalanche-l1/erc20-bridge/01-token-bridging/02-bridge-architecture) --- title: Bridge Architecture description: Get familiar with different bridge mechanisms updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Asset bridging refers to the mechanism that allows assets, such as cryptocurrencies or tokens, to move from one blockchain to another. This is particularly important in ecosystems where multiple blockchains are used for different purposes, and users want to leverage assets across these chains. Bridging effectively extends the usability of assets beyond their native blockchain, facilitating greater liquidity and integration across various platforms. ## Bridge Mechanisms ### Lock & Mint Locking: The asset is locked in a smart contract on the source blockchain. For example, if you want to bridge AVAX from Avalanche's C-chain to Ethereum, you would lock your AVAX in a smart contract on Avalanche. Minting: Simultaneously, some event notifies Ethereum that an equivalent amount of a wrapped token (e.g., Wrapped AVAX) must be minted on the target blockchain and sent to the user’s address. ### Burn & Mint Burning: When transferring assets back to the source blockchain, the wrapped tokens are burned or destroyed on the target blockchain. Releasing: The original assets are then released from the smart contract on the source blockchain back to the user's address. ### Custodians Some bridges use a custodian or a centralized party to manage the assets. This party locks the asset on one blockchain and releases it on another, relying on trust and security measures. ### Cross-Chain Communication Advanced bridges such as Avalanche Interchain Token Transfer utilize native cross-chain communication protocols to facilitate transactions between blockchains without requiring intermediaries. These protocols ensure that the asset's state and ownership are synchronized across different chains. ## Why Bridging - Enhanced Liquidity: Bridging increases the liquidity of assets by allowing them to be used across different DeFi platforms and blockchain networks. This enhances trading opportunities and financial activities. - Interoperability: It fosters interoperability between different blockchains, enabling users to access a broader range of services and applications. - Flexibility: Users can move assets to chains with lower fees, faster transaction times, or better functionalities, optimizing their experience and strategies. # Use a Demo Bridge (/academy/avalanche-l1/erc20-bridge/01-token-bridging/03-use-a-demo-bridge) --- title: Use a Demo Bridge description: Interact with the ohmywarp bridge updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- This guide will walk you through the process of using a bridge between 2 Avalanche blockchains, providing a step-by-step approach to ensure a smooth and secure experience. Go to [ohmywarp.com](https://ohmywarp.com) and connect any web3 [wallet](https://core.app). Make sure your wallet has at least some AVAX on the Fuji Testnet. **Getting testnet AVAX:** - **Recommended:** Create a [Builder Hub account](https://build.avax.network/login) and connect your wallet to receive testnet AVAX automatically - **Alternative:** Use the [external faucet](https://core.app/tools/testnet-faucet/?subnet=c&token=c) with coupon code `avalanche-academy` Mint some `TLP` token on `C-Chain` in the Mint tab. This is an ERC20 deployed on Fuji's C-chain. Finally bridge some `TLP` to `Dispatch`. You can confirm the transfer in the Mint tab. # Bridge Hacks (/academy/avalanche-l1/erc20-bridge/01-token-bridging/04-bridge-hacks) --- title: Bridge Hacks description: Learn about the most common bridge hacks. updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## What are Bridge Hacks? Asset bridges are vital for blockchain interoperability but have been the target of significant security breaches. The complexities of bridging assets between different blockchains can create vulnerabilities that malicious actors exploit. Here’s an overview of some of the most common types of bridge hacks and security issues: ### Smart Contract Exploits Smart contract exploits involve vulnerabilities in the bridge's code that can be exploited by attackers to steal assets. Common issues include: **Reentrancy Attacks**: Exploiting a contract’s ability to call itself, allowing attackers to repeatedly withdraw funds before the contract’s state is updated. Example: The DAO hack in 2016 utilized reentrancy to drain millions of dollars of ETH from a smart contract. **Arithmetic Errors**: Bugs related to integer overflows or underflows that can lead to unintended behavior. Example: In 2020, the **Value DeFi** exploit used an arithmetic bug to drain $6 million from the protocol. **Logic Flaws**: Errors in the contract's logic that can be exploited to bypass security controls. Example: In the case of bZx exploit, an attacker exploited a logic flaw to manipulate the price of assets and steal funds. ### Centralized Bridge Attacks Centralized bridges rely on a single custodian or entity to manage the assets. If the custodian is compromised, it can lead to: 1. **Theft of Assets**: Direct theft from the custodian's reserves if their security is breached. Example: The Poly Network hack in 2021 involved a vulnerability that allowed attackers to exploit the bridge's central control mechanisms and steal over $600 million. Although much of the stolen funds were later returned, it highlighted significant risks in centralized bridges. 2. **Mismanagement or Fraud**: The custodian could mismanage funds or engage in fraudulent activities. Example: Various cases of mismanagement or fraud in smaller, less established bridges where the custodian's integrity is in question. ### Governance Attacks Governance attacks target the mechanisms by which decisions are made in decentralized bridges: **Vote Manipulation**: Attacking the governance process to make changes that benefit the attacker. Example: In some DeFi protocols, attackers have manipulated governance votes to gain control or access to assets. **51% Attacks**: Gaining control over a majority of the governance or network nodes to disrupt or exploit the bridge. Example: While more common in blockchains, similar attacks can occur in decentralized bridges with weak governance structures. ### Cross-Chain Communication Vulnerabilities Cross-chain communication vulnerabilities involve weaknesses in the protocols or mechanisms that facilitate communication between different blockchains: **Data Manipulation**: Exploiting the data transmitted between chains to falsify transactions or asset states. Example: In 2022, the Wormhole bridge was exploited due to a vulnerability in its data verification process, leading to a loss of over $320 million. **Consensus Issues**: Problems with how different chains agree on the state of assets or transactions can lead to discrepancies and exploitation. Example: Misalignment between chains can lead to double-spending or other issues if not properly managed. ### Phishing and Social Engineering Phishing and social engineering attacks target users or administrators rather than the bridge’s technical infrastructure: **Phishing**: Attacking users to steal their private keys or credentials to access funds. Example: Users may be tricked into entering their private keys on fraudulent websites pretending to be bridge interfaces. **Social Engineering**: Manipulating individuals involved in the bridge’s operations to gain unauthorized access or influence. Example: Administrators may be tricked into giving away critical access or credentials. ## Mitigation Strategies 1. **Audits and Code Reviews**: Regularly audit smart contracts and bridge code to identify and fix vulnerabilities before they can be exploited. 2. **Security Best Practices**: Implement security best practices, including proper error handling, using well-tested libraries, and following coding standards. 3. **Decentralization**: Where possible, use decentralized bridging solutions to reduce the risk associated with centralized custodians. 4. **Governance Safeguards**: Strengthen governance mechanisms and ensure a robust process for decision-making and voting. 5. **User Education**: Educate users about phishing and social engineering threats to reduce the risk of these types of attacks. 6. **Insurance and Compensation**: Use insurance mechanisms or compensation funds to mitigate the impact of potential losses from breaches. By understanding and addressing these common bridge hacks, developers and users can better protect their assets and improve the overall security of cross-chain bridging solutions. # Avalanche Interchain Token Transfer (/academy/avalanche-l1/erc20-bridge/02-avalanche-interchain-token-transfer/01-avalanche-interchain-token-transfer) --- title: Avalanche Interchain Token Transfer description: Learn how to transfer assets between Avalanche L1s updated: 2024-05-31 authors: [ashucoder9] icon: Book --- The Avalanche Interchain Token Transfer is an application that allows users to transfer tokens between Avalanche L1s. The bridge is a set of smart contracts that are deployed across multiple Avalanche L1s, and leverages [Interchain Messaging](https://github.com/ava-labs/teleporter) for cross-chain communication. # What you will learn In this chapter you will learn: - **Bridge Design:** How the bridge is designed on a high level. - **File Structure:** The structure of the bridge contracts and their dependencies. - **Token Home:** What the token home is and how it works. - **Token Remote:** What the token remote is and how it works. # Interchain Token Transfer Design (/academy/avalanche-l1/erc20-bridge/02-avalanche-interchain-token-transfer/02-bridge-design) --- title: Interchain Token Transfer Design description: Get familiar with the ICTT design updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- Each token transferrer instance consists of one "home" contract and at least one but possibly many "remote" contracts. Each home contract instance manages one asset to be transferred out to `TokenRemote` instances. The home contract lives on the Avalanche L1 where the asset to be transferred exists. A transfer involves locking the asset as collateral on the home Avalanche L1 and minting a representation of the asset on the remote Avalanche L1. The remote contracts, each with a single specified home contract, live on other Avalanche L1s that want to import the asset transferred by their specified home. The token transferrers are designed to be permissionless: anyone can register compatible `TokenRemote` instances to transfer tokens from the `TokenHome` instance to that new `TokenRemote` instance. The home contract keeps track of token balances transferred to each `TokenRemote` instance and handles returning the original tokens to the user when assets are transferred back to the `TokenHome` instance. `TokenRemote` instances are registered with their home contract via a Interchain Messaging message upon creation. Home contract instances specify the asset to be transferred as either an ERC20 token or the native token, and they allow for transferring the token to any registered `TokenRemote` instances. The token representation on the remote chain can also either be an ERC20 or native token, allowing users to have any combination of ERC20 and native tokens between home and remote chains: - `ERC20` -> `ERC20` - `ERC20` -> `Native` - `Native` -> `ERC20` - `Native` -> `Native` The remote tokens are designed to have compatibility with the token transferrer on the home chain by default, and they allow custom logic to be implemented in addition. For example, developers can inherit and extend the `ERC20TokenRemote` contract to add additional functionality, such as a custom minting, burning, or transfer logic. # File Structure (/academy/avalanche-l1/erc20-bridge/02-avalanche-interchain-token-transfer/03-file-structure) --- title: File Structure description: Understand the components of the ICTT structure updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## Contract Structure The ERC20 and native token transferrers built on top of Interchain Messaging are composed of interfaces and abstract contracts that make them extendable to new implementations in the future. ### `ITokenTransferrer` Interface that defines the events token transfer contract implementations must emit. Also defines the message types and formats of messages between all implementations. ### `IERC20TokenTransferrer` and `INativeTokenTransferrer` Interfaces that define the external functions for interacting with token transfer contract implementations of each type. ERC20 and native token transferrer interfaces vary from each other in that the native token transferrer functions are `payable` and do not take an explicit amount parameter (it is implied by `msg.value`), while the ERC20 token transferrer functions are not `payable` and require the explicit amount parameter. Otherwise, they include the same functions. # Token Home (/academy/avalanche-l1/erc20-bridge/02-avalanche-interchain-token-transfer/04-token-home) --- title: Token Home description: Learn about the Token Home, the asset origin component of the ICTT updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## `TokenHome` An abstract implementation of `ITokenTransferrer` for a token transfer contract on the "home" chain with the asset to be transferred. Each `TokenHome` instance supports transferring exactly one token type (ERC20 or native) on its chain to arbitrarily many "remote" instances on other chains. It handles locking tokens to be sent to `TokenRemote` instances, as well as receiving token transfer messages to either redeem tokens it holds as collateral (i.e. unlock) or route them to other `TokenRemote` instances (i.e. "multi-hop"). In the case of a multi-hop transfer, the `TokenHome` already has the collateral locked from when the tokens were originally transferred to the first `TokenRemote` instance, so it simply updates the accounting of the transferred balances to each respective `TokenRemote` instance. Remote contracts must first be registered with a `TokenHome` instance before the home contract will allow for sending tokens to them. This is to prevent tokens from being transferred to invalid remote addresses. Anyone is able to deploy and register remote contracts, which may have been modified from this repository. It is the responsibility of the users of the home contract to independently evaluate each remote for its security and correctness. ### `ERC20TokenHome` A concrete implementation of `TokenHome` and `IERC20TokenTransferrer` that handles the locking and releasing of an ERC20 token. ### `NativeTokenHome` A concrete implementation of `TokenHome` and `INativeTokenTransferrer` that handles the locking and release of the native EVM asset. # Token Remote (/academy/avalanche-l1/erc20-bridge/02-avalanche-interchain-token-transfer/05-token-remote) --- title: Token Remote description: Learn about the Token Remote, the asset destination component of the ICTT updated: 2024-05-31 authors: [ashucoder9] icon: BookOpen --- ## `TokenRemote` An abstract implementation of `ITokenTransferrer` for a token transfer contract on a "remote" chain that receives transferred assets from a specific `TokenHome` instance. Each `TokenRemote` instance has a single `TokenHome` instance that it receives token transfers from to mint tokens. It also handles sending messages (and correspondingly burning tokens) to route tokens back to other chains (either its `TokenHome`, or other `TokenRemote` instances). Once deployed, a `TokenRemote` instance must be registered with its specified `TokenHome` contract. This is done by calling `registerWithHome` on the remote contract, which will send a Interchain Messaging message to the home contract with the information to register. All messages sent by `TokenRemote` instances are sent to the specified `TokenHome` contract, whether they are to redeem the collateral from the `TokenHome` instance or route the tokens to another `TokenRemote` instance. Routing tokens from one `TokenRemote` instance to another is referred to as a "multi-hop", where the tokens are first sent back to their `TokenHome` contract to update its accounting, and then automatically routed on to their intended destination `TokenRemote` instance. TokenRemote contracts allow for scaling token amounts, which should be used when the remote asset has a higher or lower denomination than the home asset, such as allowing for a ERC20 home asset with a denomination of 6 to be used as the native EVM asset on a remote chain (with a denomination of 18). ### `ERC20TokenRemote` A concrete implementation of `TokenRemote`, `IERC20TokenTransferrer`, and `IERC20` that handles the minting and burning of an ERC20 asset. Note that the `ERC20TokenRemote` contract is an ERC20 implementation itself, which is why it takes the `tokenName`, `tokenSymbol`, and `tokenDecimals` in its constructor. All of the ERC20 interface implementations are inherited from the standard OpenZeppelin ERC20 implementation and can be overridden in other implementations if desired. ### `NativeTokenRemote` A concrete implementation of `TokenRemote`, `INativeTokenTransferrer`, and `IWrappedNativeToken` that handles the minting and burning of the native EVM asset on its chain using the native minter precompile. Deployments of this contract must be permitted to mint native coins in the chain's configuration. Note that the `NativeTokenRemote` is also an implementation of `IWrappedNativeToken` itself, which is why the `nativeAssetSymbol` must be provided in its constructor. `NativeTokenRemote` instances always have a denomination of 18, which is the denomination of the native asset of EVM chains. We will cover Native Token Remote in depth later in the course. # ERC-20 to ERC-20 Bridge (/academy/avalanche-l1/erc20-bridge/03-erc-20-to-erc-20-bridge/01-erc-20-to-erc-20-bridge) --- title: ERC-20 to ERC-20 Bridge description: Transfer ERC-20 tokens between Avalanche L1s updated: 2024-05-31 authors: [ashucoder9] icon: Book --- import { Step, Steps } from 'fumadocs-ui/components/steps'; import Link from 'next/link'; import { buttonVariants } from '@/components/ui/button.tsx' ## Transfer an ERC-20 Token → Echo as an ERC-20 Token This chapter will show you how to send an ERC-20 Token from C-Chain to Echo using Interchain Messaging and Toolbox. This guide is conducted on the Fuji testnet, where we'll bridge tokens from C-Chain to Echo. **All Avalanche Interchain Token Transfer contracts and interfaces implemented in this chapter implementation are maintained in the [`avalanche-interchain-token-transfer`](https://github.com/ava-labs/avalanche-interchain-token-transfer/tree/main/contracts/src) repository.** Deep dives on each template interface can be found [here](https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/contracts/README.md). _Disclaimer: The avalanche-interchain-token-transfer contracts used in this tutorial are under active development and are not yet intended for production deployments. Use at your own risk._ ## What we will do 1. Deploy an ERC-20 Contract on C-Chain 2. Deploy the Interchain Token Transferer Contracts on C-Chain and Echo 3. Register Remote Token contract with the Home Transferer contract 4. Add Collateral and Start Sending Tokens # Deploy an ERC-20 (/academy/avalanche-l1/erc20-bridge/03-erc-20-to-erc-20-bridge/02-deploy-erc-20-token) --- title: Deploy an ERC-20 description: Deploy the asset to transfer updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Step, Steps } from 'fumadocs-ui/components/steps'; ### Deploy an ERC-20 Token We've already deployed an ERC-20 token in the [Transfer an ERC-20 Token](/academy/interchain-token-transfer/03-tokens/08-transfer-an-erc-20-token) section. If you've completed that step, you can use the same token for this bridge. If you haven't deployed a token yet, make sure you're connected to the Fuji testnet since we're covering this guide from Fuji to Echo. You can deploy the original ERC-20 token below: Make sure to save the deployed token address as you'll need it for the next steps. You can find it in the deployment confirmation or by checking your Core Wallet's token list. ### Add Token to Core Wallet If you haven't already added the token to your Core Wallet: 1. Go to the Tokens tab 2. Click "Manage" 3. Click "+ Add Custom Token" 4. Enter the token contract address 5. The token symbol and decimals should be automatically detected 6. Click "Add Token" Remember that you need to be connected to the Fuji testnet to see and interact with your token. ### Verify Token Balance To verify your token balance: 1. In Core Wallet, select your token from the token list 2. The balance should be displayed 3. You can also check the token balance on the [Fuji Explorer](https://testnet.snowtrace.io) by entering your address If you deployed the example token, you should see a balance of 10,000,000,000 tokens in your wallet. # Deploy a Home Contract (/academy/avalanche-l1/erc20-bridge/03-erc-20-to-erc-20-bridge/03-deploy-home) --- title: Deploy a Home Contract description: Deploy the Token Home on C-chain updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Step, Steps } from 'fumadocs-ui/components/steps'; We will deploy two Avalanche Interchain Token Transfer contracts. One on the source chain (which is C-chain in our case) and another on the destination chain (Echo in our case). ### Deploy ERC20Home Contract Since we're covering an ERC20 > ERC20 bridge, make sure to set the Transferrer type to "ERC20" and input the ERC20 token address you previously deployed in the token address field. Then deploy the ERC20Home contract on the Fuji testnet using our toolbox: Make sure you have: 1. Deployed your ERC-20 token (from [Deploy ERC-20 Token](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/02-deploy-erc-20-token)) 2. Have enough test AVAX for gas fees ### Save the ERC-20 Home Address After deployment, you'll need to save the contract address for future steps. You can find it in the deployment confirmation in the toolbox Keep this address handy as you'll need it for the next steps in the bridging process. # Deploy a Remote Contract (/academy/avalanche-l1/erc20-bridge/03-erc-20-to-erc-20-bridge/04-deploy-remote) --- title: Deploy a Remote Contract description: Deploy the Token Remote on your own blockchain updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Step, Steps } from 'fumadocs-ui/components/steps'; To ensure the wrapped token is bridged into the destination chain (in this case, C-Chain) you'll need to deploy a _remote_ contract that implements the `IERC20Bridge` interface, as well as inheriting the properties of `TeleporterTokenRemote`. In order for the bridged tokens to have all the normal functionality of a locally deployed ERC20 token, this remote contract must also inherit the properties of a standard `ERC20` contract. ### Get Test ECH Before deploying the remote contract, ensure you have some test tokens on Echo: - **Recommended:** Create a [Builder Hub account](https://build.avax.network/login) and connect your wallet to receive testnet tokens automatically on Echo - **Alternative:** Use the [external faucet](https://core.app/tools/testnet-faucet/?subnet=echo&token=echo) to claim tokens ### Deploy the Remote Contract Now we'll deploy the ERC20TokenRemote contract to the Echo chain. Use our toolbox: Make sure you have: 1. Deployed your ERC-20 token (from [Deploy ERC-20 Token](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/02-deploy-erc-20-token)) 2. Deployed your ERC20Home contract (from [Deploy Home](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/03-deploy-home)) 3. Have enough test ECH for gas fees ### Save the Remote Contract Address After deployment, you'll need to save the contract address for future steps. You can find it in the deployment confirmation in the toolbox. Keep this address handy as you'll need it for the next steps in the bridging process. # Register Remote Bridge (/academy/avalanche-l1/erc20-bridge/03-erc-20-to-erc-20-bridge/05-register-remote) --- title: Register Remote Bridge description: Register the remote bridge with the home bridge updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Step, Steps } from 'fumadocs-ui/components/steps'; After deploying both the home and remote bridge contracts, you need to register the remote bridge with the home bridge. This registration process informs the Home Bridge about your destination blockchain and bridge settings. ### Register Remote Bridge To register your remote bridge with the home bridge, use our toolbox: Make sure you have: 1. Deployed your ERC-20 token (from [Deploy ERC-20 Token](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/02-deploy-erc-20-token)) 2. Deployed your ERC20Home contract (from [Deploy Home](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/03-deploy-home)) 3. Deployed your ERC20TokenRemote contract (from [Deploy Remote](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/04-deploy-remote)) 4. Have enough test ECH for gas fees ### Verify Registration After registration, you can verify the process was successful by looking for the registration event in the transaction logs. You can find the registration confirmation in the toolbox. The registration process is a one-time setup that establishes the connection between your home and remote bridges. Once completed, you can proceed with token transfers. # Transfer Tokens (/academy/avalanche-l1/erc20-bridge/03-erc-20-to-erc-20-bridge/06-transfer-tokens) --- title: Transfer Tokens description: Transfer an ERC20 from C-chain to your own blockchain updated: 2024-05-31 authors: [ashucoder9] icon: Terminal --- import { Step, Steps } from 'fumadocs-ui/components/steps'; ### Transfer the Token Cross-chain Now that all the bridge contracts have been deployed and registered, you can transfer tokens between chains using our toolbox: Make sure you have: 1. Deployed your ERC-20 token (from [Deploy ERC-20 Token](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/02-deploy-erc-20-token)) 2. Deployed your ERC20Home contract (from [Deploy Home](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/03-deploy-home)) 3. Deployed your ERC20TokenRemote contract (from [Deploy Remote](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/04-deploy-remote)) 4. Registered your remote bridge with the home bridge (from [Register Remote](/academy/interchain-token-transfer/06-erc-20-to-erc-20-bridge/05-register-remote)) 5. Have enough test AVAX for gas fees ### Verify the Transfer After initiating the transfer, you can verify it was successful by: 1. Checking the transaction status in the toolbox 2. Checking the transaction status in Core Wallet 3. Viewing the transaction details on the [Fuji Explorer](https://testnet.snowtrace.io) 4. Checking your token balance in Core Wallet on the destination chain The transfer process may take a few moments to complete as it involves cross-chain communication. You can track the progress through the transaction hash. # Integrate ICTT with Core (/academy/avalanche-l1/erc20-bridge/03-erc-20-to-erc-20-bridge/07-avacloud-and-core-bridge) --- title: Integrate ICTT with Core description: Learn how to integrate ICTT bridges into the Core Bridge through AvaCloud. updated: 2024-05-31 icon: Book authors: [owenwahlgren] --- ## Integrate Interchain Token Transfers (ICTT) Into Core ICTT bridges deployed through [**AvaCloud**](https://avacloud.io/) will automatically integrate into the [**Core Bridge**](https://core.app/en/bridge). This ensures that any bridges created through AvaCloud are available immediately and do not need extra review. However, **ICTT bridges** deployed outside of AvaCloud (by third-party developers or other methods) will need to be submitted for manual review. Developers will need to provide: 1. **Token Bridge Contract Address(es)**: The bridge contract(s) on the L1. 2. **Layer 1 Information**: Name and other key details of the associated L1 blockchain. The Core team will review this info to ensure the bridge meets security and compliance standards. This guarantees network reliability while allowing more flexibility. ### [Community Submission Form](https://forms.gle/jdcKcYWu26CsY6jRA)