# Polkadart - The Complete Dart/Flutter SDK for Polkadot ::u-page-hero #title [Build Blockchain Apps with Polkadot]{.text-5xl} #description Type-safe, performant, and production-ready SDK for building decentralized applications on Polkadot and Substrate-based chains. One codebase for mobile, web, and desktop. #links :::u-button --- color: primary size: xl to: https://polkadart.dev/introduction trailing-icon: i-lucide-arrow-right --- Start Building ::: :::u-button --- color: neutral icon: i-simple-icons-github size: xl target: _blank to: https://github.com/leonardocustodio/polkadart variant: outline --- Star on GitHub ::: #headline :::u-badge{.text-sm.px-3.py-1 color="primary" variant="subtle"} The Complete Dart/Flutter SDK ::: :: ::div{.flex.justify-center.items-center.gap-8.mt-8} :::div [**12** Packages]{.text-sm.text-gray-600} ::: :::div [**13+** Contributors]{.text-sm.text-gray-600} ::: :::div [**100%** Dart Native]{.text-sm.text-gray-600} ::: :::div [**Web3** Foundation Grant]{.text-sm.text-gray-600} ::: :: ::u-page-section #title Why Choose Polkadart? #description Polkadart is a comprehensive Dart/Flutter SDK for Polkadot, providing everything you need to build robust blockchain applications with type safety and excellent developer experience. :::u-page-grid ::::u-page-card --- class: text-left --- #title :icon{.w-7.h-7.inline-block.mr-3.text-primary.align-middle name="i-heroicons-code-bracket"} [Developer-First Design]{.text-xl.font-semibold.mb-2.align-middle} #description Built by developers for developers, with intuitive APIs, excellent documentation, and comprehensive examples. :::: ::::u-page-card --- class: text-left --- #title :icon{.w-7.h-7.inline-block.mr-3.text-primary.align-middle name="i-heroicons-shield-check"} [100% Type-Safe]{.text-xl.font-semibold.mb-2.align-middle} #description Auto-generated types from chain metadata ensure compile-time safety and eliminate runtime errors. :::: ::::u-page-card --- class: text-left --- #title :icon{.w-7.h-7.inline-block.mr-3.text-primary.align-middle name="i-heroicons-device-phone-mobile"} [True Cross-Platform]{.text-xl.font-semibold.mb-2.align-middle} #description Write once, deploy everywhere. Native performance on iOS, Android, Web, macOS, Windows, and Linux. :::: ::::u-page-card --- class: text-left --- #title :icon{.w-7.h-7.inline-block.mr-3.text-primary.align-middle name="i-heroicons-bolt"} [Lightning Fast]{.text-xl.font-semibold.mb-2.align-middle} #description Native Dart compilation and optimized SCALE codec deliver exceptional performance on all platforms. :::: ::::u-page-card --- class: text-left --- #title :icon{.w-7.h-7.inline-block.mr-3.text-primary.align-middle name="i-heroicons-cube-transparent"} [Smart Contract Ready]{.text-xl.font-semibold.mb-2.align-middle} #description Full support for ink! smart contracts with type-safe contract calls and event handling. :::: ::::u-page-card --- class: text-left --- #title :icon{.w-7.h-7.inline-block.mr-3.text-primary.align-middle name="i-heroicons-check-badge"} [Well-Maintained]{.text-xl.font-semibold.mb-2.align-middle} #description Actively developed with comprehensive test coverage, regular updates, and responsive community support. :::: ::: :: # Introduction ## Welcome to Polkadart Polkadart is a powerful and type-safe Dart/Flutter SDK that provides developers with everything needed to build decentralized applications on Polkadot, Substrate, and other compatible blockchain networks. With comprehensive APIs, automatic code generation, and Flutter-first design, it's the most developer-friendly way to integrate blockchain functionality into your Dart applications. All Polkadart packages are available on [GitHub](https://github.com/leonardocustodio/polkadart){rel="nofollow"} with a [permissive Apache 2.0 license](https://github.com/leonardocustodio/polkadart/blob/main/LICENSE){rel="nofollow"}. ## πŸ“¦ Package Ecosystem Polkadart is a modular ecosystem where each package serves a specific purpose. You can use them independently or together based on your needs. ### Core Packages | Package | Description | Version | | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | [**polkadart**](https://pub.dev/packages/polkadart){rel="nofollow"} | Main SDK for blockchain interaction - provides APIs for connecting to nodes, querying state, and sending transactions | [![pub](https://img.shields.io/pub/v/polkadart.svg)](https://pub.dev/packages/polkadart){rel="nofollow"} | | [**polkadart\_keyring**](https://pub.dev/packages/polkadart_keyring){rel="nofollow"} | Secure key management and account handling - create wallets, manage keypairs, and sign transactions | [![pub](https://img.shields.io/pub/v/polkadart_keyring.svg)](https://pub.dev/packages/polkadart_keyring){rel="nofollow"} | | [**polkadart\_cli**](https://pub.dev/packages/polkadart_cli){rel="nofollow"} | Command-line tool for generating typed APIs from chain metadata | [![pub](https://img.shields.io/pub/v/polkadart_cli.svg)](https://pub.dev/packages/polkadart_cli){rel="nofollow"} | ### Encoding & Cryptography | Package | Description | Version | | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | [**polkadart\_scale\_codec**](https://pub.dev/packages/polkadart_scale_codec){rel="nofollow"} | SCALE (Simple Concatenated Aggregate Little-Endian) codec - pure Dart implementation for encoding/decoding blockchain data | [![pub](https://img.shields.io/pub/v/polkadart_scale_codec.svg)](https://pub.dev/packages/polkadart_scale_codec){rel="nofollow"} | | [**substrate\_metadata**](https://pub.dev/packages/substrate_metadata){rel="nofollow"} | Comprehensive metadata types, codecs for encoding/decoding storage, extrinsics, events, and constants | [![pub](https://img.shields.io/pub/v/substrate_metadata.svg)](https://pub.dev/packages/substrate_metadata){rel="nofollow"} | | [**ss58**](https://pub.dev/packages/ss58){rel="nofollow"} | SS58 address format encoder/decoder for Substrate addresses | [![pub](https://img.shields.io/pub/v/ss58.svg)](https://pub.dev/packages/ss58){rel="nofollow"} | ### Cryptographic Primitives | Package | Description | Version | | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | [**sr25519**](https://pub.dev/packages/sr25519){rel="nofollow"} | Schnorrkel/Ristretto SR25519 signature scheme (Polkadot's default) | [![pub](https://img.shields.io/pub/v/sr25519.svg)](https://pub.dev/packages/sr25519){rel="nofollow"} | | [**secp256k1\_ecdsa**](https://pub.dev/packages/secp256k1_ecdsa){rel="nofollow"} | SECP256k1 elliptic curve for ECDSA signatures (Ethereum compatibility) | [![pub](https://img.shields.io/pub/v/secp256k1_ecdsa.svg)](https://pub.dev/packages/secp256k1_ecdsa){rel="nofollow"} | | [**substrate\_bip39**](https://pub.dev/packages/substrate_bip39){rel="nofollow"} | BIP39 mnemonic phrase generation for HD wallets | [![pub](https://img.shields.io/pub/v/substrate_bip39.svg)](https://pub.dev/packages/substrate_bip39){rel="nofollow"} | ### Smart Contracts | Package | Description | Version | | ---------------------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | [**ink\_abi**](https://pub.dev/packages/ink_abi){rel="nofollow"} | ink! smart contract ABI encoder/decoder | [![pub](https://img.shields.io/pub/v/ink_abi.svg)](https://pub.dev/packages/ink_abi){rel="nofollow"} | | [**ink\_cli**](https://pub.dev/packages/ink_cli){rel="nofollow"} | CLI tool for generating typed interfaces from ink! contract metadata | [![pub](https://img.shields.io/pub/v/ink_cli.svg)](https://pub.dev/packages/ink_cli){rel="nofollow"} | ### Light Client | Package | Description | Version | | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | [**smoldot**](https://pub.dev/packages/smoldot){rel="nofollow"} | Dart wrapper for smoldot-light, providing a lightweight Polkadot/Substrate client via Rust FFI bindings | [![pub](https://img.shields.io/pub/v/smoldot.svg)](https://pub.dev/packages/smoldot){rel="nofollow"} | ## Contributing We welcome contributions to the Polkadart project. If you see something that needs improvement or a new feature, please open an issue or submit a pull request. We follow the [Contributor Covenant](https://www.contributor-covenant.org/version/2/0/code_of_conduct/){rel="nofollow"} code of conduct. ## Contributors ## Supporters ![Web3 foundation grants program](https://raw.githubusercontent.com/w3f/Grants-Program/00855ef70bc503433dc9fccc057c2f66a426a82b/static/img/badge_black.svg) We extend our deepest gratitude to the Web3 Foundation for their invaluable support in furthering the development of the Polkadart project. With your support, we have been able to expand our library, offering a robust set of tools that foster innovation and growth within the Web3 ecosystem. Thank you for partnering with us on this journey towards a decentralized future. # Why Polkadart? ## The Power of Dart Meets Blockchain Polkadart brings together the best of both worlds: Dart's modern development experience and Polkadot's innovative blockchain technology. Here's why developers choose Polkadart for their decentralized applications. ## 🎯 Key Advantages ::card :::card Generated types from chain metadata ensure compile-time safety and eliminate runtime errors ::: :::card Single codebase runs on iOS, Android, Web, Desktop, and Server environments ::: :::card Auto-completion, inline documentation, and familiar Dart syntax speed up development ::: :::card Native compilation and efficient SCALE codec implementation for blazing-fast execution ::: ## πŸ’™ Why Dart for Blockchain? ### Familiar and Approachable Dart's clean syntax makes blockchain development accessible: ```dart // Simple and readable transaction code final transfer = await polkadot.tx.balances .transfer( dest: recipientAddress, value: BigInt.from(1000000000000), // 1 DOT ) .signAndSend(keyring); ``` ### Built for Production - **Sound null safety** prevents common bugs - **Strong typing** catches errors early - **Async/await** simplifies complex blockchain operations - **Hot reload** speeds up development (Flutter) ### One Language, Many Platforms Write once, deploy everywhere: - πŸ“± **Mobile apps** with Flutter - 🌐 **Web applications** with Flutter Web - πŸ–₯ **Desktop software** with Flutter Desktop - ☁️ **Backend services** with Dart server ## 🌟 Why Polkadart over Alternatives? ### vs. JavaScript/TypeScript Libraries | Feature | Polkadart | JS/TS Libraries | | -------------- | ------------------------- | ----------------- | | Type Safety | βœ… Compile-time guaranteed | ⚠️ Runtime checks | | Performance | βœ… Native compilation | ❌ Interpreted | | Mobile Support | βœ… First-class Flutter | ⚠️ React Native | | Bundle Size | βœ… Tree-shaking optimized | ❌ Large bundles | | IDE Support | βœ… Full IntelliSense | βœ… Good support | ### vs. Native SDKs (Swift/Kotlin) | Feature | Polkadart | Native SDKs | | -------------- | ----------------- | -------------------- | | Cross-platform | βœ… All platforms | ❌ Platform-specific | | Code Reuse | βœ… 100% shared | ❌ Duplicate effort | | Learning Curve | βœ… One language | ❌ Multiple languages | | Maintenance | βœ… Single codebase | ❌ Multiple codebases | | Community | βœ… Growing fast | ⚠️ Platform-specific | ## πŸ’‘ Perfect For :::card ::::card Build trading platforms, wallets, and financial tools with real-time data :::: ::::card Create marketplaces with seamless blockchain integration :::: ::::card Integrate blockchain features into mobile and web games :::: ::::card Deploy secure, scalable blockchain solutions for business :::: ## πŸ”§ Powerful Features ### Complete Blockchain Toolkit - **Transaction Building**: Type-safe extrinsic construction - **Storage Queries**: Efficient data retrieval with automatic decoding - **Event Subscriptions**: Real-time blockchain monitoring - **Key Management**: Secure wallet and signing operations - **Smart Contracts**: Full ink! contract support ### Developer-Friendly Design ```dart // Intuitive API design final balance = await polkadot.query.system.account(address); print('Free balance: ${balance.data.free}'); // Subscribe to events await polkadot.query.system.events((events) { for (final event in events) { print('Event: ${event.event.name}'); } }); ``` ## 🌈 Ecosystem Integration Polkadart works seamlessly with: - **Polkadot & Kusama**: Full mainnet support - **Parachains**: Any Substrate-based chain - **Smart Contracts**: ink! and EVM compatible - **IPFS**: Decentralized storage integration - **Hardware Wallets**: Ledger support (coming soon) ## πŸ“ˆ Growing Community ::::callout{type="tip"} Join our vibrant community of developers building the future of Web3 with Dart! :::: ::: :: - Active development with regular updates - Comprehensive documentation and examples - Responsive community support - Open-source and transparent development - Backed by Web3 Foundation grants ## 🎯 Start Building Today Ready to build your next blockchain application with Polkadart? ::card :::card [Get started in 5 minutes β†’](https://polkadart.dev/getting-started/installation) ::: :::card [Browse code examples β†’](https://polkadart.dev/guides/overview) ::: :::card [Explore the API β†’](https://polkadart.dev/api/overview) ::: :::card [Join on Matrix β†’](https://matrix.to/#/#polkadart:matrix.org){rel="nofollow"} ::: --- \*\*Still have questions?\*\* Check out our \[FAQ]\(/support-community) or reach out on \[GitHub]\(https\://github.com/leonardocustodio/polkadart). :: # Connecting to a Network After installing Polkadart and generating types, you're ready to connect to a network and start querying the blockchain. ## Setting Up Your Connection ### Using Provider with Generated APIs Create a new file `bin/demo.dart` and add the following code: ```dart [demo.dart] Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final query = await polkadot.rpc.state.getRuntimeVersion(); print(query.toJson()); } ``` ### Running Your Application Execute your application with: ```bash dart run bin/demo.dart ``` ### Expected Output You should see the runtime version of the Polkadot chain: ```dart { specName: polkadot, implName: parity-polkadot, authoringVersion: 0, specVersion: 1003003, implVersion: 0, apis: [...], transactionVersion: 26, stateVersion: 1 } ``` πŸŽ‰ **Success!** You've successfully connected to Polkadot and made your first query to the chain! ## Connecting to Different Networks ::tabs :::tab{label="Polkadot"} ```dart final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); ``` ::::tab{label="Kusama"} ```dart final provider = Provider.fromUri(Uri.parse('wss://kusama-rpc.polkadot.io')); ``` :::::tab{label="Westend (Testnet)"} ```dart final provider = Provider.fromUri(Uri.parse('wss://westend-rpc.polkadot.io')); ``` ::::::tab{label="Local Node"} ```dart final provider = Provider.fromUri(Uri.parse('ws://127.0.0.1:9944')); ``` :::::: ::::: :::: ::: :: ::callout{type="tip"} **Pro Tip:** For production applications, consider using multiple RPC endpoints for redundancy and load balancing. :: ## Connection Options You can customize your connection with additional options: ```dart final provider = Provider.fromUri( Uri.parse('wss://rpc.polkadot.io'), // Add connection options here if needed ); ``` ## Next Steps - [Learn about decoding chain data β†’](https://polkadart.dev/getting-started/decoding) - [Explore the Polkadart API β†’](https://polkadart.dev/api/overview) - [See example guides β†’](https://polkadart.dev/guides/overview) # Decoding Chain Data When working with blockchain data, all information is stored in an encoded format for efficiency. Polkadart provides powerful tools to decode this data into human-readable formats that your Dart applications can easily work with. ## Understanding Storage Encoding Blockchain storage uses SCALE (Simple Concatenated Aggregate Little-Endian) encoding. This includes: - **Storage keys**: Hashed identifiers for storage locations - **Storage values**: The actual encoded data - **Account IDs**: 32-byte public keys ## Querying and Decoding Storage ### Complete Example Create a new file `bin/demo.dart` with the following code: ```dart [demo.dart] {10-13,16-17,19-20} Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); // Step 1: Get the storage key prefix for accounts final accountMapPrefix = polkadot.query.system.accountMapPrefix(); // Step 2: Query the first 10 account storage keys final keys = await polkadot.rpc.state.getKeysPaged( key: accountMapPrefix, count: 10 ); print("First 10 account storage keys:"); print(keys.map((key) => '0x${hex.encode(key)}').join('\n'))); // Step 3: Decode account IDs from storage keys // Storage keys are composed of: [pallet_hash(16 bytes) + storage_item_hash(16 bytes) + account_id(32 bytes)] final accountIds = keys.map((key) => const AccountId32Codec().decode(ByteInput(key.sublist(32))) ); print("\nFirst 10 account public keys:"); print(accountIds.map((account) => '0x${hex.encode(account)}').join('\n'))); // Step 4: Query account information for each account final accountInfos = await Future.wait( accountIds.map((account) => polkadot.query.system.account(account)) ); // Step 5: Display decoded account information print("\nAccount Information:"); for (final (index, accountInfo) in accountInfos.indexed) { print('Account ${index + 1}:'); print(' Nonce: ${accountInfo.nonce}'); print(' Free Balance: ${accountInfo.data.free}'); print(' Reserved: ${accountInfo.data.reserved}'); print(' Frozen: ${accountInfo.data.frozen}'); print(''); } } ``` ### Running the Example ```bash dart run bin/demo.dart ``` ### Understanding the Output The output shows: ::step ## Storage Keys: Long hexadecimal strings that identify storage locations :::step ## Account IDs: 32-byte public keys extracted from the storage keys ::::step ## Account Information: Decoded data including: - `nonce`: Number of transactions sent from this account - `free`: Available balance - `reserved`: Balance locked for specific purposes - `frozen`: Balance that cannot be transferred :::::callout{type="note"} The storage key structure is: - First 16 bytes: Pallet identifier hash - Next 16 bytes: Storage item identifier hash - Remaining bytes: The actual key data (account ID in this case) ::::: :::: ::: ## Key Concepts ### Storage Keys Every piece of data on the blockchain has a unique storage key. Polkadart helps you: - Generate the correct storage keys - Query data using these keys - Decode the returned values ### SCALE Codec The SCALE codec is used throughout Polkadot for efficient data encoding: ```dart // Decode any SCALE-encoded data final decoded = SomeTypeCodec().decode(ByteInput(encodedData)); // Encode data to SCALE format final encoded = SomeTypeCodec().encode(myData); ``` ### Type Safety Generated types ensure you're working with the correct data structures: ```dart // Type-safe query with auto-completion final balance = await polkadot.query.system.account(accountId); // balance is strongly typed as AccountInfo ``` πŸŽ‰ **Congratulations!** You've successfully queried and decoded blockchain data! ## Next Steps - [Explore the API documentation β†’](https://polkadart.dev/api/overview) - [Learn to make transfers β†’](https://polkadart.dev/guides/make-transfer) - [Listen to real-time events β†’](https://polkadart.dev/guides/listen-blocks) :: # Installation ## Creating Your First Project Let's create a sample application together: ```bash dart create demo ``` ## Installing Polkadart Packages Now let's add Polkadart packages. You can install all packages for comprehensive functionality, or just the ones you need: ::tabs :::tab{label="Install All Packages"} ```bash dart pub add polkadart polkadart_cli polkadart_keyring polkadart_scale_codec secp256k1_ecdsa sr25519 ss58 substrate_bip39 substrate_metadata ``` ::::tab{label="Core Only"} ```bash # Essential packages for basic functionality dart pub add polkadart polkadart_cli polkadart_keyring ``` :::::tab{label="Custom Selection"} ```bash # Choose packages based on your needs: dart pub add polkadart # Core SDK dart pub add polkadart_cli # Type generation dart pub add polkadart_keyring # Key management # Add other packages as needed ``` ::::: :::: ::: :: ## Package Configuration After installation, your `pubspec.yaml` should contain these dependencies: ```yaml [pubspec.yaml] name: demo version: 1.0.0 environment: sdk: ^3.8.0 dependencies: # Core packages polkadart: ^1.0.0 # Main SDK for blockchain interaction polkadart_cli: ^1.0.0 # CLI tool for code generation polkadart_keyring: ^0.7.2 # Key management and signing # Encoding and cryptography polkadart_scale_codec: ^2.0.0 # SCALE codec implementation substrate_metadata: ^2.0.0 # Metadata parsing and decoding ss58: ^2.0.0 # Address encoding # Cryptographic primitives (optional) secp256k1_ecdsa: ^0.6.3 # ECDSA signatures sr25519: ^0.7.2 # SR25519 signatures substrate_bip39: ^0.7.2 # Mnemonic phrases # Smart contracts (optional) ink_abi: ^1.0.0 # ink! ABI encoder/decoder ink_cli: ^1.0.0 # ink! CLI tool ``` ## Next Steps - [Configure the type generator β†’](https://polkadart.dev/getting-started/type-generator) - [Connect to a network β†’](https://polkadart.dev/getting-started/connecting) - [Learn about decoding chain data β†’](https://polkadart.dev/getting-started/decoding) # Type Generator Type generation is a crucial part of working with Polkadot and Substrate-based chains. Each network has its own unique set of types, APIs, and extrinsics. Polkadart's CLI tool automatically generates these types from chain metadata, providing: - βœ… **Type safety** - Catch errors at compile time - πŸ’‘ **Auto-completion** - IDE support for all chain-specific APIs - πŸ”„ **Always up-to-date** - Regenerate when chains upgrade - 🎯 **Chain-specific** - Accurate types for each network ## Configuration ### Setting Up Type Generation Add the following configuration to your `pubspec.yaml` file: ```yaml [pubspec.yaml] {1-5} polkadart: output_dir: lib/generated # Where to generate the code chains: polkadot: wss://rpc.polkadot.io # Polkadot mainnet kusama: wss://kusama-rpc.polkadot.io # Kusama canary network # Add more chains as needed: # moonbeam: wss://wss.api.moonbeam.network # acala: wss://acala-rpc.dwellir.com ``` ::callout{type="tip"} You can add any Substrate-based chain by providing its WebSocket RPC endpoint. :: ## Generating Types ::steps :::step ## Run the generator command: ```bash dart run polkadart_cli:generate -v ``` The `-v` flag enables verbose output to see the generation progress. ::::step ## Check the generated files: A `lib/generated` folder will be created with a subfolder for each chain: ```text lib/ └── generated/ β”œβ”€β”€ polkadot/ β”‚ β”œβ”€β”€ polkadot.dart # Main entry point β”‚ β”œβ”€β”€ types/ # Chain-specific types β”‚ β”œβ”€β”€ pallets/ # Pallet APIs β”‚ └── extrinsics/ # Transaction builders └── kusama/ └── ... # Similar structure ``` :::::step ## Import and use the generated code: ```dart import 'package:your_app/generated/polkadot/polkadot.dart'; ``` ::::: :::: ::: :: ## Advanced Configuration ### Custom Output Directory ```yaml [pubspec.yaml] polkadart: output_dir: lib/blockchain # Custom output path ``` ### Using Environment Variables For security, you can use environment variables for RPC endpoints: ```yaml [pubspec.yaml] polkadart: output_dir: lib/generated chains: polkadot: ${POLKADOT_RPC_URL} kusama: ${KUSAMA_RPC_URL} ``` ## Updating Generated Types When a chain performs a runtime upgrade, regenerate the types: ```bash # Clean old generated files rm -rf lib/generated # Regenerate with latest metadata dart run polkadart_cli:generate -v ``` ::callout{type="caution"} Always regenerate types after chain runtime upgrades to ensure compatibility. :: ## Next Steps - [Connect to a network β†’](https://polkadart.dev/getting-started/connecting) - [Learn about the generated APIs β†’](https://polkadart.dev/api/overview) - [Explore example guides β†’](https://polkadart.dev/guides/overview) # Overview ## What is substrate\_metadata? The `substrate_metadata` package is a fundamental component of the Polkadart ecosystem that provides everything needed to work with Substrate runtime metadata. It enables you to decode and encode blockchain data including extrinsics, events, storage entries, and constants. Think of it as the bridge between the raw blockchain data and your Dart application - it knows how to interpret the metadata from any Substrate-based chain and provides the tools to encode/decode all types of blockchain data. ## Key Features ::card :::card ### Metadata Support Full support for both V14 and V15 metadata formats with automatic version detection ::: :::card ### Type Registry Intelligent type resolution with caching for optimal performance ::: :::card ### Derived Codecs Pre-built codecs for events, extrinsics, calls, and constants ::: :::card ### Storage Hashers Complete implementation of all Substrate storage hashing algorithms ::: :::card ### Metadata Merkleization RFC-78 compliant metadata hash generation for secure offline signing ::: :::card ### Chain Info Facade Convenient high-level API for common metadata operations ::: ## Core Components The package is organized into several key modules: ### Metadata Types The metadata system provides complete type definitions for both V14 and V15 runtime metadata: - **RuntimeMetadataPrefixed**: Top-level metadata container with magic number and version - **PalletMetadata**: Pallet-specific metadata including calls, events, storage, and constants - **PortableType**: Type registry entries with full type definitions - **ExtrinsicMetadata**: Metadata for transaction structure and signed extensions ### Type Registry The `MetadataTypeRegistry` is the heart of the package: ```dart // Create registry from metadata bytes final prefixed = RuntimeMetadataPrefixed.codec.decode(input); final registry = MetadataTypeRegistry(prefixed); // Get codec for any type ID final codec = registry.codecFor(42); final value = codec.decode(input); // Access pallets and their components final pallet = registry.palletByName('System'); final constantValue = registry.getConstantValue('System', 'BlockHashCount'); ``` Key features: - Automatic V14/V15 metadata handling - Intelligent codec caching for performance - Recursive type support via proxy pattern - O(1) pallet lookups ### Derived Codecs Pre-built specialized codecs for common blockchain data: - **EventsRecordCodec**: Decode event records from storage - **ExtrinsicsCodec**: Decode extrinsics from blocks - **RuntimeCallCodec**: Encode/decode runtime calls - **ConstantsCodec**: Access pallet constants ### ChainInfo Facade The `ChainInfo` class provides a convenient one-stop interface: ```dart // Create from metadata bytes final chainInfo = ChainInfo.fromMetadata(metadataBytes); // Decode events final events = chainInfo.decodeEvents(eventBytes); // Decode extrinsics final extrinsics = chainInfo.decodeExtrinsics(extrinsicBytes); // Access constants final value = chainInfo.getConstant('System', 'BlockHashCount'); // Access registry for advanced operations final customCodec = chainInfo.registry.codecFor(typeId); ``` ### Storage Hashers Complete implementation of all Substrate storage hashing algorithms: - **Blake2\_128** - **Blake2\_256** - **Blake2\_128Concat** - **Twox128** - **Twox256** - **Twox64Concat** - **Identity** These are used for generating storage keys for querying chain state. ### Metadata Merkleization RFC-78 compliant implementation for generating metadata hashes: ```dart final merkleizer = MetadataMerkleizer.fromMetadata( metadata, decimals: 10, tokenSymbol: 'DOT', ); // Get the metadata hash final metadataHash = hex.encode(merkleizer.digest()); // Generate proof for extrinsic final proof = merkleizer.getProofForExtrinsic(extrinsic, additionalSigned); ``` This enables secure offline transaction signing by proving the metadata used. ## Installation Add substrate\_metadata to your `pubspec.yaml`: ```yaml dependencies: substrate_metadata: ^2.0.0 ``` ## Quick Start Here's a complete example of using substrate\_metadata: ```dart import 'dart:typed_data'; import 'package:substrate_metadata/substrate_metadata.dart'; import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; void main() async { // 1. Decode metadata from bytes final metadataBytes = Uint8List.fromList([...]); // Your metadata bytes final chainInfo = ChainInfo.fromMetadata(metadataBytes); print('Metadata version: ${chainInfo.version}'); print('Number of pallets: ${chainInfo.pallets.length}'); // 2. Access pallet information final systemPallet = chainInfo.pallet('System'); print('System pallet index: ${systemPallet?.index}'); // 3. Get a constant value final blockHashCount = chainInfo.getConstant('System', 'BlockHashCount'); print('BlockHashCount: $blockHashCount'); // 4. Decode events final eventBytes = Uint8List.fromList([...]); // Event data from storage final events = chainInfo.decodeEvents(eventBytes); for (final eventRecord in events) { print('Event: ${eventRecord.event.pallet}.${eventRecord.event.name}'); print('Phase: ${eventRecord.phase}'); } // 5. Decode extrinsics from a block final extrinsicBytes = Uint8List.fromList([...]); // Extrinsic data final extrinsics = chainInfo.decodeExtrinsics(extrinsicBytes); for (final extrinsic in extrinsics) { print('Extrinsic version: ${extrinsic.version}'); print('Signed: ${extrinsic.isSigned}'); print('Call: ${extrinsic.call.pallet}.${extrinsic.call.name}'); } // 6. Access the registry for advanced operations final accountIdType = chainInfo.registry.accountIdType; final accountIdCodec = chainInfo.registry.codecFor(accountIdType); // Use the codec to decode account IDs final accountId = accountIdCodec.decode(Input.fromBytes(accountBytes)); } ``` ## Use Cases The substrate\_metadata package is essential for: - Building blockchain explorers - Creating wallet applications - Developing dApps that need to decode chain data - Implementing custom RPC clients - Offline transaction signing - Chain indexers and monitoring tools ## Architecture The package follows a layered architecture: 1. **Metadata Layer**: Raw metadata types (V14/V15) 2. **Registry Layer**: Type resolution and codec management 3. **Derived Codecs Layer**: Specialized codecs for events, extrinsics, etc. 4. **Facade Layer**: High-level ChainInfo API 5. **Utils Layer**: Hashers, merkleization, and utilities This design provides both low-level control and high-level convenience. ## Next Steps - [Working with Metadata](https://polkadart.dev/substrate-metadata/metadata) - [Using the Type Registry](https://polkadart.dev/substrate-metadata/type-registry) - [Decoding Events and Extrinsics](https://polkadart.dev/substrate-metadata/codecs) - [Storage Hashers](https://polkadart.dev/substrate-metadata/storage-hashers) - [Metadata Merkleization](https://polkadart.dev/substrate-metadata/merkleization) :: # Working with Metadata ## Understanding Runtime Metadata Runtime metadata is a comprehensive description of a blockchain's capabilities. It contains everything your application needs to know to interact with the chain, including: - Available pallets (modules) - Storage structure and types - Callable functions (extrinsics) - Events that can be emitted - Constants defined in the runtime - Type definitions for all data structures ## Metadata Versions The substrate\_metadata package supports two metadata versions: ### Metadata V14 The original production-ready metadata format: ```dart import 'package:substrate_metadata/metadata/metadata.dart'; // Decode V14 metadata final prefixed = RuntimeMetadataPrefixed.codec.decode(input); if (prefixed.metadata is RuntimeMetadataV14) { final v14 = prefixed.metadata as RuntimeMetadataV14; print('Pallets: ${v14.pallets.length}'); print('Types: ${v14.types.length}'); } ``` ### Metadata V15 Enhanced metadata format with additional features: - Runtime APIs metadata - Custom metadata section - Outer enums for better type organization ```dart if (prefixed.metadata is RuntimeMetadataV15) { final v15 = prefixed.metadata as RuntimeMetadataV15; print('Runtime APIs: ${v15.apis.length}'); print('Outer enums: ${v15.outerEnums.callType}'); } ``` ## Metadata Structure ### RuntimeMetadataPrefixed The top-level container for all metadata: ```dart class RuntimeMetadataPrefixed { final int magicNumber; // Should be 0x6d657461 ('meta') final RuntimeMetadata metadata; // V14 or V15 bool get isValidMagicNumber => magicNumber == 0x6d657461; } ``` Example usage: ```dart // Decode metadata final input = Input.fromBytes(metadataBytes); final prefixed = RuntimeMetadataPrefixed.codec.decode(input); // Validate if (!prefixed.isValidMagicNumber) { throw Exception('Invalid metadata magic number'); } // Get version print('Metadata version: ${prefixed.metadata.version}'); ``` ### Pallet Metadata Each pallet in the runtime has its own metadata: ```dart // Get pallet by name final systemPallet = metadata.pallets.firstWhere( (p) => p.name == 'System', ); print('Pallet index: ${systemPallet.index}'); print('Has storage: ${systemPallet.storage != null}'); print('Has calls: ${systemPallet.calls != null}'); print('Has events: ${systemPallet.event != null}'); print('Number of constants: ${systemPallet.constants.length}'); ``` ### Storage Metadata Storage items define how data is stored on-chain: ```dart final storage = systemPallet.storage; for (final entry in storage.entries) { print('Storage name: ${entry.name}'); print('Modifier: ${entry.modifier}'); // Optional, Default, or Required // Check storage type switch (entry.type) { case StorageEntryTypePlain(:final valueType): print('Plain storage, value type ID: $valueType'); break; case StorageEntryTypeMap(:final hashers, :final keyType, :final valueType): print('Map storage'); print('Key type ID: $keyType'); print('Value type ID: $valueType'); print('Hashers: ${hashers.map((h) => h.name).join(", ")}'); break; } } ``` ### Event Metadata Events that can be emitted by pallets: ```dart final eventMetadata = systemPallet.event; if (eventMetadata != null) { print('Event type ID: ${eventMetadata.type}'); // Get the actual event variants from the type registry final eventType = registry.typeById(eventMetadata.type); if (eventType.type.typeDef is TypeDefVariant) { final variants = (eventType.type.typeDef as TypeDefVariant).variants; for (final variant in variants) { print('Event: ${variant.name}'); print('Fields: ${variant.fields.length}'); } } } ``` ### Call Metadata Callable functions (extrinsics) defined by pallets: ```dart final callMetadata = systemPallet.calls; if (callMetadata != null) { print('Call type ID: ${callMetadata.type}'); // Get the actual call variants final callType = registry.typeById(callMetadata.type); if (callType.type.typeDef is TypeDefVariant) { final variants = (callType.type.typeDef as TypeDefVariant).variants; for (final variant in variants) { print('Call: ${variant.name}'); print('Parameters: ${variant.fields.length}'); } } } ``` ### Constants Metadata Runtime constants defined by pallets: ```dart for (final constant in systemPallet.constants) { print('Constant: ${constant.name}'); print('Type ID: ${constant.type}'); print('Docs: ${constant.docs.join(" ")}'); // Decode the constant value final codec = registry.codecFor(constant.type); final value = codec.decode(Input.fromBytes(constant.value)); print('Value: $value'); } ``` ## Extrinsic Metadata Metadata about the transaction format: ```dart final extrinsicMetadata = metadata.extrinsic; print('Version: ${extrinsicMetadata.version}'); print('Address type: ${extrinsicMetadata.addressType}'); print('Call type: ${extrinsicMetadata.callType}'); print('Signature type: ${extrinsicMetadata.signatureType}'); // Signed extensions (transaction extras) for (final ext in extrinsicMetadata.signedExtensions) { print('Extension: ${ext.identifier}'); print('Type: ${ext.type}'); print('Additional signed: ${ext.additionalSigned}'); } ``` ## V15 Specific Features ### Runtime APIs V15 metadata includes information about runtime APIs: ```dart if (metadata is RuntimeMetadataV15) { for (final api in metadata.apis) { print('API: ${api.name}'); for (final method in api.methods) { print(' Method: ${method.name}'); print(' Inputs: ${method.inputs.length}'); print(' Output type: ${method.output}'); } } } ``` ### Outer Enums V15 provides top-level enum types for better organization: ```dart if (metadata is RuntimeMetadataV15) { final outerEnums = metadata.outerEnums; print('Call type: ${outerEnums.callType}'); print('Event type: ${outerEnums.eventType}'); print('Error type: ${outerEnums.errorType}'); } ``` ## Complete Example Here's a comprehensive example that explores all metadata: ```dart import 'package:substrate_metadata/substrate_metadata.dart'; void exploreMetadata(Uint8List metadataBytes) { // Decode metadata final input = Input.fromBytes(metadataBytes); final prefixed = RuntimeMetadataPrefixed.codec.decode(input); final registry = MetadataTypeRegistry(prefixed); print('=== Metadata Information ==='); print('Version: ${registry.version}'); print('Magic number valid: ${prefixed.isValidMagicNumber}'); print(''); print('=== Pallets ==='); for (final pallet in registry.pallets) { print('Pallet: ${pallet.name} (index: ${pallet.index})'); // Storage if (pallet.storage != null) { print(' Storage items: ${pallet.storage!.entries.length}'); } // Calls if (pallet.calls != null) { final callType = registry.typeById(pallet.calls!.type); if (callType.type.typeDef is TypeDefVariant) { final variants = (callType.type.typeDef as TypeDefVariant).variants; print(' Calls: ${variants.length}'); } } // Events if (pallet.event != null) { final eventType = registry.typeById(pallet.event!.type); if (eventType.type.typeDef is TypeDefVariant) { final variants = (eventType.type.typeDef as TypeDefVariant).variants; print(' Events: ${variants.length}'); } } // Constants print(' Constants: ${pallet.constants.length}'); print(''); } print('=== Extrinsic Format ==='); final extrinsic = registry.extrinsic; print('Version: ${extrinsic.version}'); print('Signed extensions: ${extrinsic.signedExtensions.length}'); print(''); // V15 specific if (registry.version == 15) { final metadata = prefixed.metadata as RuntimeMetadataV15; print('=== Runtime APIs (V15) ==='); for (final api in metadata.apis) { print('${api.name}: ${api.methods.length} methods'); } } } ``` ## Best Practices ::callout{type="tip"} ### Cache the Registry Creating a `MetadataTypeRegistry` is expensive. Create it once and reuse it for all operations. :: ::callout{type="tip"} ### Validate Metadata Always check the magic number and version before using metadata. :: ::callout{type="caution"} ### Handle Version Differences Not all chains use V15. Always check the version and handle both V14 and V15. :: ::callout{type="tip"} ### Use ChainInfo For common operations, use the `ChainInfo` facade instead of working with raw metadata directly. :: ## Next Steps - [Type Registry](https://polkadart.dev/substrate-metadata/type-registry) - Learn about type resolution and codec management - [Decoding Data](https://polkadart.dev/substrate-metadata/codecs) - Decode events and extrinsics - [Storage Access](https://polkadart.dev/substrate-metadata/storage-hashers) - Work with chain storage # Type Registry ## What is the Type Registry? The `MetadataTypeRegistry` is the central component for working with types in Substrate metadata. It acts as a type resolution engine that: - Resolves type IDs to their definitions - Creates and caches codecs for encoding/decoding - Handles recursive types safely - Provides efficient lookups for pallets and their components Think of it as a smart factory that knows how to build the right codec for any type in the metadata. ## Creating a Registry ```dart import 'package:substrate_metadata/substrate_metadata.dart'; // From raw metadata bytes final input = Input.fromBytes(metadataBytes); final prefixed = RuntimeMetadataPrefixed.codec.decode(input); final registry = MetadataTypeRegistry(prefixed); // Or use ChainInfo which includes a registry final chainInfo = ChainInfo.fromMetadata(metadataBytes); final registry = chainInfo.registry; ``` The registry automatically: - Validates the magic number - Checks version compatibility (V14 or V15) - Builds pallet indices for O(1) lookups - Initializes caching structures ## Core Type Resolution ### Getting Codecs by Type ID The most fundamental operation is getting a codec for a type: ```dart // Get codec for a specific type ID final codec = registry.codecFor(42); // Use the codec to decode data final value = codec.decode(input); // Or encode data final encoded = codec.encode(myData); ``` The registry automatically handles: - Primitive types (u8, u32, bool, etc.) - Composite types (structs) - Variant types (enums) - Sequences (Vec) - Arrays - Tuples - Compact encoding - Recursive types ### Type Caching Codecs are expensive to create, so the registry caches them: ```dart // First call - creates and caches final codec1 = registry.codecFor(42); // Second call - returns cached version final codec2 = registry.codecFor(42); // Same instance assert(identical(codec1, codec2)); // Check cache statistics final stats = registry.cacheStats; print('Cached codecs: ${stats.codecCacheSize}'); print('Cached paths: ${stats.typePathCacheSize}'); ``` ### Handling Recursive Types The registry safely handles recursive types using a proxy pattern: ```dart // Example: A linked list type that references itself // The registry detects recursion and uses ProxyCodec final listCodec = registry.codecFor(linkedListTypeId); // The codec works correctly despite the recursion final list = listCodec.decode(input); ``` ## Type Lookups ### By Type ID Direct lookup by type ID: ```dart final portableType = registry.typeById(42); print('Type path: ${portableType.type.path.join("::")}'); print('Type params: ${portableType.type.params.length}'); // Access the type definition switch (portableType.type.typeDef) { case TypeDefPrimitive(:final primitive): print('Primitive: ${primitive.name}'); case TypeDefComposite(:final fields): print('Composite with ${fields.length} fields'); case TypeDefVariant(:final variants): print('Enum with ${variants.length} variants'); // ... other cases } ``` ### By Type Path Lookup by the type's full path (slower, but cached): ```dart // Find type by path final optionType = registry.typeByPath('Option'); final resultType = registry.typeByPath('Result'); final accountIdType = registry.typeByPath('sp_core::crypto::AccountId32'); // Get codec by path final codec = registry.codecForPath('Option'); ``` ::callout{type="caution"} Path lookups are expensive on first call (linear search through all types). Results are cached for subsequent calls. :: ## Pallet Access The registry provides efficient pallet lookups: ### By Name (O(1)) ```dart final systemPallet = registry.palletByName('System'); final balancesPallet = registry.palletByName('Balances'); if (systemPallet != null) { print('Index: ${systemPallet.index}'); print('Storage items: ${systemPallet.storage?.entries.length ?? 0}'); } ``` ### By Index (O(1)) ```dart final pallet = registry.palletByIndex(0); // Usually System ``` ### All Pallets ```dart for (final pallet in registry.pallets) { print('${pallet.name} (${pallet.index})'); } ``` ### Pallet Component Types Get type IDs for pallet components: ```dart // Calls type final callTypeId = registry.getPalletCallType('Balances'); if (callTypeId != null) { final callCodec = registry.codecFor(callTypeId); } // Events type final eventTypeId = registry.getPalletEventType('System'); // Errors type final errorTypeId = registry.getPalletErrorType('Balances'); ``` ## Storage Access ### Storage Metadata Get metadata for a storage entry: ```dart final storage = registry.getStorageMetadata('System', 'Account'); if (storage != null) { print('Name: ${storage.name}'); print('Modifier: ${storage.modifier}'); print('Default: ${storage.defaultValue}'); // Check storage type switch (storage.type) { case StorageEntryTypePlain(:final valueType): print('Plain storage, value type: $valueType'); case StorageEntryTypeMap(:final hashers, :final keyType, :final valueType): print('Map storage'); print('Key type: $keyType'); print('Value type: $valueType'); print('Hashers: $hashers'); } } ``` ### Storage Type ID Get just the type ID: ```dart final typeId = registry.getStorageType('System', 'Account'); if (typeId != null) { final codec = registry.codecFor(typeId); // Use codec to decode storage value } ``` ### Storage Hashers Get the hashers used for a storage map: ```dart final hashers = registry.getStorageHashers('System', 'Account'); // hashers contains the list of StorageHasherEnum values ``` ## Constants Access ### Constant Metadata ```dart final constant = registry.getConstantMetadata('System', 'BlockHashCount'); if (constant != null) { print('Name: ${constant.name}'); print('Type: ${constant.type}'); print('Documentation: ${constant.docs.join(" ")}'); } ``` ### Direct Value Access Get the decoded constant value directly: ```dart final blockHashCount = registry.getConstantValue('System', 'BlockHashCount'); print('BlockHashCount: $blockHashCount'); final existentialDeposit = registry.getConstantValue('Balances', 'ExistentialDeposit'); print('ExistentialDeposit: $existentialDeposit'); ``` ## Variant and Composite Resolution ### Get Variant by Name For enum types: ```dart final callType = registry.getPalletCallType('Balances'); if (callType != null) { final transferVariant = registry.getVariant(callType, 'transfer'); if (transferVariant != null) { print('Variant index: ${transferVariant.index}'); print('Fields: ${transferVariant.fields.length}'); for (final field in transferVariant.fields) { print('Field: ${field.name ?? "unnamed"}, type: ${field.type}'); } } } ``` ### Get Variant by Index ```dart final variant = registry.getVariantByIndex(callType, 0); ``` ### Get Composite Fields For struct types: ```dart final fields = registry.getFields(typeId); if (fields != null) { for (final field in fields) { print('Field: ${field.name ?? "unnamed"}'); print('Type: ${field.type}'); print('Type name: ${field.typeName}'); } } ``` ## Extrinsic & Common Types ### Extrinsic Metadata ```dart final extrinsic = registry.extrinsic; print('Version: ${extrinsic.version}'); print('Address type: ${registry.accountIdType}'); print('Signature type: ${registry.signatureType}'); print('Call type: ${registry.callType}'); print('Extra type: ${registry.extraType}'); ``` ### Signed Extensions ```dart final extensions = registry.signedExtensions; for (final ext in extensions) { print('Extension: ${ext.identifier}'); print('Type: ${ext.type}'); print('Additional signed: ${ext.additionalSigned}'); } ``` ## V15 Specific Features When working with V15 metadata: ### Outer Enums ```dart final outerEnums = registry.outerEnums; if (outerEnums != null) { final callCodec = registry.codecFor(outerEnums.callType); final eventCodec = registry.codecFor(outerEnums.eventType); final errorCodec = registry.codecFor(outerEnums.errorType); } ``` ### Runtime APIs ```dart final contractsApi = registry.getRuntimeApi('ContractsApi'); if (contractsApi != null) { print('Methods: ${contractsApi.methods.length}'); for (final method in contractsApi.methods) { print('Method: ${method.name}'); } } ``` ### Runtime API Methods ```dart final method = registry.getRuntimeApiMethod('ContractsApi', 'call'); if (method != null) { print('Inputs: ${method.inputs.length}'); print('Output type: ${method.output}'); // Get output codec final outputCodec = registry.codecFor(method.output); } ``` Or use the convenience method: ```dart final codec = registry.getRuntimeApiOutputCodec('ContractsApi', 'call'); if (codec != null) { final result = codec.decode(responseBytes); } ``` ## Utility Methods ### Type Checking ```dart // Check if a type is Option if (registry.isOption(typeId)) { print('This is an Option type'); } // Check if a type is Result if (registry.isResult(typeId)) { print('This is a Result type'); } // Check if a type is Vec if (registry.isSequence(typeId)) { print('This is a sequence/Vec type'); } ``` ### Cache Management ```dart // Get cache statistics final stats = registry.cacheStats; print('Total cached items: ${stats.totalCachedItems}'); print('Codecs: ${stats.codecCacheSize}'); print('Type paths: ${stats.typePathCacheSize}'); print('Proxy codecs: ${stats.proxyCodecCount}'); // Clear all caches (rarely needed) registry.clearCache(); ``` ## Complete Example Here's a comprehensive example using the registry: ```dart import 'package:substrate_metadata/substrate_metadata.dart'; void demonstrateRegistry(Uint8List metadataBytes) { // Create registry final chainInfo = ChainInfo.fromMetadata(metadataBytes); final registry = chainInfo.registry; print('=== Basic Info ==='); print('Version: ${registry.version}'); print('Pallets: ${registry.pallets.length}'); print('\n=== Decode Account Info ==='); // Get the Account storage type final accountTypeId = registry.getStorageType('System', 'Account'); if (accountTypeId != null) { final codec = registry.codecFor(accountTypeId); final accountInfo = codec.decode(accountDataInput); print('Account data: $accountInfo'); } print('\n=== Decode an Event ==='); final eventTypeId = registry.outerEnums?.eventType ?? registry.getPalletEventType('System'); if (eventTypeId != null) { final eventCodec = registry.codecFor(eventTypeId); // Use the codec to decode events } print('\n=== Access Constants ==='); final ss58Prefix = registry.getConstantValue('System', 'SS58Prefix'); print('SS58 Prefix: $ss58Prefix'); final existentialDeposit = registry.getConstantValue('Balances', 'ExistentialDeposit'); print('Existential Deposit: $existentialDeposit'); print('\n=== Cache Statistics ==='); final stats = registry.cacheStats; print('Cached codecs: ${stats.codecCacheSize}'); print('Cached paths: ${stats.typePathCacheSize}'); } ``` ## Performance Tips ::callout{type="tip"} ### Reuse the Registry Create the registry once and reuse it. Creating a new registry is expensive. :: ::callout{type="tip"} ### Prefer Type IDs When possible, use type IDs instead of paths. ID lookups are O(1), path lookups are O(n). :: ::callout{type="tip"} ### Cache Lookup Results If you're looking up the same pallet/storage/constant repeatedly, cache the results. :: ::callout{type="tip"} ### Use ChainInfo for Common Operations For standard operations, use `ChainInfo` instead of directly using the registry. :: ## Next Steps - [Codecs for Events and Extrinsics](https://polkadart.dev/substrate-metadata/codecs) - [Storage Hashers](https://polkadart.dev/substrate-metadata/storage-hashers) - [Metadata Merkleization](https://polkadart.dev/substrate-metadata/merkleization) # Decoding Events and Extrinsics ## Derived Codecs Overview The substrate\_metadata package provides specialized "derived codecs" for common blockchain data structures. These codecs are built dynamically from the metadata and handle all the complexity of decoding events, extrinsics, and calls. ## Available Codecs - **EventsRecordCodec**: Decode event records from storage - **ExtrinsicsCodec**: Decode extrinsics from blocks - **RuntimeCallCodec**: Encode/decode runtime calls - **ConstantsCodec**: Access pallet constants - **RuntimeEventsCodec**: Decode individual events - **UncheckedExtrinsicCodec**: Decode individual extrinsics ## Quick Start with ChainInfo The easiest way to use codecs is through the `ChainInfo` facade: ```dart import 'package:substrate_metadata/substrate_metadata.dart'; final chainInfo = ChainInfo.fromMetadata(metadataBytes); // Decode events from storage final events = chainInfo.decodeEvents(eventBytes); // Decode extrinsics from a block final extrinsics = chainInfo.decodeExtrinsics(extrinsicBytes); // Get a constant final value = chainInfo.getConstant('System', 'BlockHashCount'); ``` ## Decoding Events ### Event Record Structure An event record contains: ```dart class EventRecord { final Phase phase; // When the event occurred final RuntimeEvent event; // The actual event final List topics; // Indexed topics } ``` ### Using EventsRecordCodec ```dart // Create codec from registry final registry = MetadataTypeRegistry(prefixed); final eventsCodec = EventsRecordCodec(registry); // Decode events from storage final input = Input.fromBytes(eventBytes); final events = eventsCodec.decode(input); // Process events for (final eventRecord in events) { print('Phase: ${eventRecord.phase}'); final event = eventRecord.event; print('Event: ${event.pallet}.${event.name}'); print('Fields: ${event.fields}'); print('Topics: ${eventRecord.topics}'); } ``` ### Event Phase The phase indicates when during block execution the event occurred: ```dart switch (eventRecord.phase) { case ApplyExtrinsic(:final index): print('Occurred during extrinsic $index'); case Finalization(): print('Occurred during finalization'); case Initialization(): print('Occurred during initialization'); } ``` ### Runtime Event Structure ```dart class RuntimeEvent { final String pallet; // e.g., "System", "Balances" final String name; // e.g., "Transfer", "ExtrinsicSuccess" final Map fields; // Event data } ``` ### Accessing Event Data ```dart for (final eventRecord in events) { final event = eventRecord.event; // Check event type if (event.pallet == 'Balances' && event.name == 'Transfer') { final from = event.fields['from']; final to = event.fields['to']; final amount = event.fields['amount']; print('Transfer: $from -> $to: $amount'); } if (event.pallet == 'System' && event.name == 'ExtrinsicSuccess') { final dispatchInfo = event.fields['dispatch_info']; print('Extrinsic succeeded: $dispatchInfo'); } } ``` ## Decoding Extrinsics ### Extrinsic Structure An extrinsic (transaction) contains: ```dart class UncheckedExtrinsic { final int version; // Extrinsic version final ExtrinsicSignature? signature; // Signature (if signed) final RuntimeCall call; // The actual call bool get isSigned => signature != null; } ``` ### Using ExtrinsicsCodec ```dart // Create codec final extrinsicsCodec = ExtrinsicsCodec(registry); // Decode extrinsics from a block final input = Input.fromBytes(blockExtrinsicsBytes); final extrinsics = extrinsicsCodec.decode(input); // Process extrinsics for (final extrinsic in extrinsics) { print('Version: ${extrinsic.version}'); print('Signed: ${extrinsic.isSigned}'); final call = extrinsic.call; print('Call: ${call.pallet}.${call.name}'); print('Arguments: ${call.fields}'); if (extrinsic.signature != null) { final sig = extrinsic.signature!; print('Signer: ${sig.address}'); print('Signature: ${sig.signature}'); print('Extra: ${sig.extra}'); } } ``` ### Extrinsic Signature For signed extrinsics: ```dart class ExtrinsicSignature { final dynamic address; // Signer address final dynamic signature; // Cryptographic signature final Map extra; // Signed extensions (nonce, tip, etc.) } ``` Example: ```dart if (extrinsic.isSigned) { final sig = extrinsic.signature!; // Access signed extensions final nonce = sig.extra['CheckNonce']; final tip = sig.extra['ChargeTransactionPayment']; final era = sig.extra['CheckMortality']; print('Nonce: $nonce'); print('Tip: $tip'); print('Era: $era'); } ``` ### Runtime Call Structure ```dart class RuntimeCall { final String pallet; // e.g., "Balances" final String name; // e.g., "transfer_keep_alive" final Map fields; // Call arguments } ``` ### Accessing Call Data ```dart final call = extrinsic.call; if (call.pallet == 'Balances' && call.name == 'transfer_keep_alive') { final dest = call.fields['dest']; final value = call.fields['value']; print('Transfer to $dest: $value'); } if (call.pallet == 'Staking' && call.name == 'bond') { final value = call.fields['value']; final payee = call.fields['payee']; print('Bond $value, payee: $payee'); } ``` ## Encoding Runtime Calls You can also encode calls for creating transactions: ```dart // Create codec final callsCodec = RuntimeCallCodec(registry); // Encode a call final call = RuntimeCall( pallet: 'Balances', name: 'transfer_keep_alive', fields: { 'dest': accountId, 'value': BigInt.from(1000000000000), }, ); final encoded = callsCodec.encode(call); // Use this encoded call in an extrinsic ``` ## Working with Constants ### Using ConstantsCodec ```dart // Create codec final constantsCodec = ConstantsCodec(registry); // Get a constant (lazy access) final lazyConstant = constantsCodec['System']['BlockHashCount']; // Decode the value final value = lazyConstant.value; print('BlockHashCount: $value'); ``` ### Direct Access Or use the registry directly: ```dart final blockHashCount = registry.getConstantValue('System', 'BlockHashCount'); final existentialDeposit = registry.getConstantValue('Balances', 'ExistentialDeposit'); ``` ## Complete Example: Block Processing Here's a complete example that processes a block: ```dart import 'package:substrate_metadata/substrate_metadata.dart'; class BlockProcessor { final ChainInfo chainInfo; BlockProcessor(this.chainInfo); void processBlock({ required Uint8List extrinsicsBytes, required Uint8List eventsBytes, }) { // Decode extrinsics final extrinsics = chainInfo.decodeExtrinsics(extrinsicsBytes); print('Block has ${extrinsics.length} extrinsics'); // Decode events final events = chainInfo.decodeEvents(eventsBytes); print('Block has ${events.length} events'); // Process each extrinsic for (var i = 0; i < extrinsics.length; i++) { final extrinsic = extrinsics[i]; print('\n=== Extrinsic $i ==='); print('Signed: ${extrinsic.isSigned}'); print('Call: ${extrinsic.call.pallet}.${extrinsic.call.name}'); if (extrinsic.isSigned) { final sig = extrinsic.signature!; print('Signer: ${sig.address}'); print('Nonce: ${sig.extra['CheckNonce']}'); } // Find events for this extrinsic print('Events:'); for (final eventRecord in events) { if (eventRecord.phase is ApplyExtrinsic) { final phase = eventRecord.phase as ApplyExtrinsic; if (phase.index == i) { final event = eventRecord.event; print(' ${event.pallet}.${event.name}'); // Check for failure if (event.pallet == 'System' && event.name == 'ExtrinsicFailed') { print(' ⚠️ Extrinsic failed!'); print(' Error: ${event.fields}'); } } } } } // Show finalization events print('\n=== Finalization Events ==='); for (final eventRecord in events) { if (eventRecord.phase is Finalization) { final event = eventRecord.event; print('${event.pallet}.${event.name}'); } } } } // Usage void main() async { final chainInfo = ChainInfo.fromMetadata(metadataBytes); final processor = BlockProcessor(chainInfo); processor.processBlock( extrinsicsBytes: blockExtrinsics, eventsBytes: blockEvents, ); } ``` ## Advanced: Custom Event Filtering ```dart class EventFilter { final ChainInfo chainInfo; EventFilter(this.chainInfo); List findTransfers(Uint8List eventsBytes) { final events = chainInfo.decodeEvents(eventsBytes); return events .map((record) => record.event) .where((event) => event.pallet == 'Balances' && (event.name == 'Transfer' || event.name == 'Deposit')) .toList(); } List findFailures(Uint8List eventsBytes) { final events = chainInfo.decodeEvents(eventsBytes); return events .map((record) => record.event) .where((event) => event.pallet == 'System' && event.name == 'ExtrinsicFailed') .toList(); } Map> groupEventsByExtrinsic(Uint8List eventsBytes) { final events = chainInfo.decodeEvents(eventsBytes); final grouped = >{}; for (final record in events) { if (record.phase is ApplyExtrinsic) { final index = (record.phase as ApplyExtrinsic).index; grouped.putIfAbsent(index, () => []).add(record.event); } } return grouped; } } ``` ## Working with Specific Event Types ### Balance Transfers ```dart void processTransferEvents(List events) { for (final record in events) { final event = record.event; if (event.pallet == 'Balances' && event.name == 'Transfer') { final from = event.fields['from']; final to = event.fields['to']; final amount = event.fields['amount']; print('Transfer: $from -> $to'); print('Amount: $amount'); } } } ``` ### Staking Events ```dart void processStakingEvents(List events) { for (final record in events) { final event = record.event; if (event.pallet == 'Staking') { switch (event.name) { case 'Bonded': print('${event.fields['stash']} bonded ${event.fields['amount']}'); case 'Unbonded': print('${event.fields['stash']} unbonded ${event.fields['amount']}'); case 'Rewarded': print('${event.fields['stash']} rewarded ${event.fields['amount']}'); } } } } ``` ## Performance Considerations ::callout{type="tip"} ### Cache Codecs Create codecs once and reuse them for multiple decode operations. :: ::callout{type="tip"} ### Use ChainInfo For most use cases, use `ChainInfo` instead of creating codecs directly. :: ::callout{type="caution"} ### Large Event Lists If you're processing many events, consider streaming or pagination to avoid loading everything into memory. :: ## Next Steps - [Storage Hashers](https://polkadart.dev/substrate-metadata/storage-hashers) - Generate storage keys - [Metadata Merkleization](https://polkadart.dev/substrate-metadata/merkleization) - Secure offline signing - [ChainInfo API](https://polkadart.dev/substrate-metadata/chain-info) - High-level convenience API # Storage Hashers ## What are Storage Hashers? In Substrate, storage keys are generated by hashing the pallet name, storage item name, and (for maps) the key. Different hashing algorithms provide different trade-offs between security, performance, and features. The substrate\_metadata package provides implementations of all Substrate storage hashers, allowing you to generate the correct storage keys for querying chain state. ## Available Hashers ### Blake2\_128 128-bit Blake2 hash: ```dart import 'package:substrate_metadata/substrate_hashers/substrate_hashers.dart'; final hasher = Hasher.blake2b128(); final hash = hasher.hash(data); // Returns 16 bytes (128 bits) ``` **Use case**: Fast, secure hashing for storage keys. ### Blake2\_256 256-bit Blake2 hash: ```dart final hasher = Hasher.blake2b256(); final hash = hasher.hash(data); // Returns 32 bytes (256 bits) ``` **Use case**: More secure variant with larger output. ### Blake2\_128Concat Blake2\_128 with the original data concatenated: ```dart final hasher = Hasher.blake2b128Concat(); final hash = hasher.hash(data); // Returns: 16-byte hash + original data ``` **Use case**: Allows iterating over storage keys while maintaining hash security. The concatenated data allows decoding the key. ### Twox128 128-bit xxHash: ```dart final hasher = Hasher.twox128(); final hash = hasher.hash(data); // Returns 16 bytes ``` **Use case**: Very fast hashing. Used for pallet and storage item names (which are not user-controlled). ::callout{type="caution"} Twox hashers are NOT cryptographically secure. Only use for non-user-controlled data. :: ### Twox256 256-bit xxHash: ```dart final hasher = Hasher.twox256(); final hash = hasher.hash(data); // Returns 32 bytes ``` **Use case**: Faster alternative to Blake2\_256 for non-user-controlled data. ### Twox64Concat 64-bit xxHash with original data concatenated: ```dart final hasher = Hasher.twox64Concat(); final hash = hasher.hash(data); // Returns: 8-byte hash + original data ``` **Use case**: Fast iteration over storage keys. NOT cryptographically secure. ### Identity No hashing, returns the data as-is: ```dart final hasher = Hasher.identity(); final hash = hasher.hash(data); // Returns original data unchanged ``` **Use case**: When you want the key itself to be readable. Often used with Blake2 hashers for composite keys. ## Generating Storage Keys ### For Simple Storage Values Simple values use a fixed storage key: ```dart import 'package:substrate_metadata/substrate_hashers/substrate_hashers.dart'; // Generate key for a simple storage value String generateStorageKey(String palletName, String storageName) { final palletHash = Hasher.twox128().hashHex(palletName); final storageHash = Hasher.twox128().hashHex(storageName); return '0x$palletHash$storageHash'; } // Example: System.Number final key = generateStorageKey('System', 'Number'); ``` ### For Storage Maps Maps need the key to be hashed too: ```dart import 'dart:typed_data'; import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; String generateMapStorageKey({ required String palletName, required String storageName, required Hasher keyHasher, required Uint8List encodedKey, }) { final palletHash = Hasher.twox128().hashHex(palletName); final storageHash = Hasher.twox128().hashHex(storageName); final keyHash = keyHasher.hashHex(encodedKey); return '0x$palletHash$storageHash$keyHash'; } // Example: System.Account (uses Blake2_128Concat) final accountId = Uint8List(32); // Your account ID final key = generateMapStorageKey( palletName: 'System', storageName: 'Account', keyHasher: Hasher.blake2b128Concat(), encodedKey: accountId, ); ``` ### For Double Maps Some storage uses two keys: ```dart String generateDoubleMapKey({ required String palletName, required String storageName, required Hasher keyHasher1, required Uint8List encodedKey1, required Hasher keyHasher2, required Uint8List encodedKey2, }) { final palletHash = Hasher.twox128().hashHex(palletName); final storageHash = Hasher.twox128().hashHex(storageName); final keyHash1 = keyHasher1.hashHex(encodedKey1); final keyHash2 = keyHasher2.hashHex(encodedKey2); return '0x$palletHash$storageHash$keyHash1$keyHash2'; } ``` ## Using Storage Metadata The metadata tells you which hashers to use: ```dart import 'package:substrate_metadata/substrate_metadata.dart'; void generateKeyFromMetadata( MetadataTypeRegistry registry, String palletName, String storageName, List keys, ) { // Get storage metadata final storage = registry.getStorageMetadata(palletName, storageName); if (storage == null) { throw Exception('Storage not found'); } // Build the base key final palletHash = Hasher.twox128().hash(utf8.encode(palletName)); final storageHash = Hasher.twox128().hash(utf8.encode(storageName)); final keyBytes = [...palletHash, ...storageHash]; // Handle storage type switch (storage.type) { case StorageEntryTypePlain(): // No additional key needed break; case StorageEntryTypeMap(:final hashers): // Apply hashers to keys if (keys.length != hashers.length) { throw Exception('Key count mismatch'); } for (var i = 0; i < keys.length; i++) { final hasher = _getHasher(hashers[i]); final keyHash = hasher.hash(keys[i]); keyBytes.addAll(keyHash); } break; } final storageKey = '0x${hex.encode(keyBytes)}'; print('Storage key: $storageKey'); } Hasher _getHasher(StorageHasherEnum hasherEnum) { return switch (hasherEnum) { Blake2b128() => Hasher.blake2b128(), Blake2b256() => Hasher.blake2b256(), Blake2b128Concat() => Hasher.blake2b128Concat(), Twox128() => Hasher.twox128(), Twox256() => Hasher.twox256(), Twox64Concat() => Hasher.twox64Concat(), Identity() => Hasher.identity(), }; } ``` ## Complete Storage Key Example Here's a complete example for querying account balance: ```dart import 'package:substrate_metadata/substrate_metadata.dart'; import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; class StorageKeyGenerator { final MetadataTypeRegistry registry; StorageKeyGenerator(this.registry); /// Generate storage key for System.Account String accountStorageKey(Uint8List accountId) { return _generateMapKey( palletName: 'System', storageName: 'Account', key: accountId, ); } /// Generate storage key for Balances.TotalIssuance String totalIssuanceKey() { return _generatePlainKey( palletName: 'Balances', storageName: 'TotalIssuance', ); } /// Generate a plain storage key (no map key) String _generatePlainKey({ required String palletName, required String storageName, }) { final palletHash = Hasher.twox128().hash(utf8.encode(palletName)); final storageHash = Hasher.twox128().hash(utf8.encode(storageName)); return '0x${hex.encode([...palletHash, ...storageHash])}'; } /// Generate a map storage key String _generateMapKey({ required String palletName, required String storageName, required Uint8List key, }) { // Get storage metadata to find the hasher final storage = registry.getStorageMetadata(palletName, storageName); if (storage == null) { throw Exception('Storage $palletName.$storageName not found'); } // Get the hasher Hasher hasher; if (storage.type case StorageEntryTypeMap(:final hashers)) { hasher = _getHasher(hashers.first); } else { throw Exception('Not a map storage'); } // Build the key final palletHash = Hasher.twox128().hash(utf8.encode(palletName)); final storageHash = Hasher.twox128().hash(utf8.encode(storageName)); final keyHash = hasher.hash(key); return '0x${hex.encode([...palletHash, ...storageHash, ...keyHash])}'; } Hasher _getHasher(StorageHasherEnum hasherEnum) { return switch (hasherEnum) { Blake2b128() => Hasher.blake2b128(), Blake2b256() => Hasher.blake2b256(), Blake2b128Concat() => Hasher.blake2b128Concat(), Twox128() => Hasher.twox128(), Twox256() => Hasher.twox256(), Twox64Concat() => Hasher.twox64Concat(), Identity() => Hasher.identity(), }; } } // Usage void main() { final chainInfo = ChainInfo.fromMetadata(metadataBytes); final keyGen = StorageKeyGenerator(chainInfo.registry); // Generate key for an account final accountId = Uint8List(32); // Fill with actual account ID final accountKey = keyGen.accountStorageKey(accountId); print('Account storage key: $accountKey'); // Generate key for total issuance final totalIssuanceKey = keyGen.totalIssuanceKey(); print('Total issuance key: $totalIssuanceKey'); } ``` ## Iterating Over Storage Keys When using concat hashers (Blake2\_128Concat, Twox64Concat, Identity), you can iterate and decode keys: ```dart import 'dart:convert'; void iterateAccounts(List storageKeys) { for (final key in storageKeys) { // Remove 0x prefix final keyBytes = hex.decode(key.substring(2)); // Skip pallet hash (16 bytes) + storage hash (16 bytes) final offset = 32; // For Blake2_128Concat: next 16 bytes are hash, rest is account ID final hashLength = 16; final accountId = keyBytes.sublist(offset + hashLength); print('Account ID: ${hex.encode(accountId)}'); } } ``` ## Hasher Comparison | Hasher | Output Size | Concat | Secure | Speed | Use Case | | ----------------- | ----------- | ------ | ------ | --------- | ----------------------- | | Blake2\_128 | 16 bytes | No | Yes | Fast | Secure hashing | | Blake2\_256 | 32 bytes | No | Yes | Fast | More secure | | Blake2\_128Concat | 16 + data | Yes | Yes | Fast | Iterable secure storage | | Twox128 | 16 bytes | No | No | Very fast | Non-user data | | Twox256 | 32 bytes | No | No | Very fast | Non-user data | | Twox64Concat | 8 + data | Yes | No | Very fast | Iterable non-user data | | Identity | Data size | Yes | No | Instant | Readable keys | ## Best Practices ::callout{type="caution"} ### Never Use Twox for User Data Twox hashers are NOT cryptographically secure. Only use them for pallet/storage names or other non-user-controlled data. :: ::callout{type="tip"} ### Use Metadata Always get hashers from metadata rather than hardcoding them. Chains may change hashers in runtime upgrades. :: ::callout{type="tip"} ### Cache Storage Keys If you're querying the same storage repeatedly, cache the generated keys. :: ::callout{type="tip"} ### Use Concat for Iteration If you need to iterate over storage keys, use concat hashers (Blake2\_128Concat, Twox64Concat). :: ## Next Steps - [Metadata Merkleization](https://polkadart.dev/substrate-metadata/merkleization) - Secure offline signing - [ChainInfo API](https://polkadart.dev/substrate-metadata/chain-info) - High-level API for common operations # Metadata Merkleization ## What is Metadata Merkleization? Metadata merkleization is a security feature defined in [RFC-78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html){rel="nofollow"} that allows offline wallets and hardware devices to verify they're using the correct metadata when signing transactions. The problem it solves: When signing transactions offline, how do you know the metadata you're using matches what's on-chain? Malicious metadata could trick you into signing something different than what you intended. The solution: Generate a cryptographic hash (merkle root) of the metadata and include it in the transaction. The chain validates this hash, ensuring the metadata matches. ## Why It Matters For secure offline signing: - Hardware wallets can verify metadata authenticity - Air-gapped devices can trust the metadata - Users can be confident they're signing what they think they're signing - Prevents metadata-based attacks ## Quick Start ```dart import 'package:substrate_metadata/substrate_metadata.dart'; // Create merkleizer from metadata final merkleizer = MetadataMerkleizer.fromMetadata( metadata, decimals: 10, // Token decimals tokenSymbol: 'DOT', // Token symbol ); // Get the metadata hash final metadataHash = hex.encode(merkleizer.digest()); // Use this hash when creating transactions ``` ## Creating a Merkleizer ### Basic Creation ```dart import 'package:substrate_metadata/substrate_metadata.dart'; // Decode metadata first final input = Input.fromBytes(metadataBytes); final prefixed = RuntimeMetadataPrefixed.codec.decode(input); final metadata = prefixed.metadata; // Create merkleizer final merkleizer = MetadataMerkleizer.fromMetadata( metadata, decimals: 10, tokenSymbol: 'DOT', ); ``` ### With Optional Parameters You can provide additional verification parameters: ```dart final merkleizer = MetadataMerkleizer.fromMetadata( metadata, decimals: 10, tokenSymbol: 'DOT', specVersion: 1003003, // Optional: verify spec version specName: 'polkadot', // Optional: verify spec name base58Prefix: 0, // Optional: verify SS58 prefix ); ``` If provided, these values will be validated against the metadata's System.Version constant. ## Generating the Metadata Hash The metadata hash (digest) is a 32-byte Blake3 hash: ```dart // Generate the hash final hashBytes = merkleizer.digest(); // Convert to hex for use in transactions final metadataHash = hex.encode(hashBytes); print('Metadata hash: 0x$metadataHash'); ``` The digest can be called multiple times - it's cached after the first call. ## Using in Transactions ### With SigningPayload When creating a signing payload, include the metadata hash: ```dart import 'package:polkadart/polkadart.dart'; final payload = SigningPayload( method: encodedCall, specVersion: runtimeVersion.specVersion, transactionVersion: runtimeVersion.transactionVersion, genesisHash: encodeHex(genesisHash), blockHash: encodeHex(currentBlockHash), blockNumber: currentBlockNumber, eraPeriod: 64, nonce: nonce, tip: 0, metadataHash: metadataHash, // ← Include the hash ).encode(registry); ``` ### With ExtrinsicPayload Also include it when building the final extrinsic: ```dart final extrinsic = ExtrinsicPayload( signer: wallet.bytes(), method: encodedCall, signature: signature, eraPeriod: 64, blockNumber: currentBlockNumber, nonce: nonce, tip: 0, metadataHash: metadataHash, // ← Include the hash ).encode(registry, SignatureType.sr25519); ``` ## Generating Proofs The merkleizer can generate proofs for specific transactions: ### Proof for Complete Extrinsic ```dart // For a fully encoded extrinsic final proof = merkleizer.getProofForExtrinsic( extrinsicBytes, additionalSignedBytes, // Can be null for unsigned ); // The proof contains the merkle path ``` ### Proof for Extrinsic Payload ```dart // For just the signing payload final proof = merkleizer.getProofForExtrinsicPayload(payloadBytes); ``` ### Proof for Extrinsic Parts For more control: ```dart final proof = merkleizer.getProofForExtrinsicParts( callData, // The encoded call includedInExtrinsic, // Encoded signed extensions includedInSignedData, // Encoded additional signed data ); ``` ## Complete Example Here's a full example of creating a transaction with metadata hash: ```dart import 'package:substrate_metadata/substrate_metadata.dart'; import 'package:polkadart/polkadart.dart'; Future transferWithMetadataHash() async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final wallet = await KeyPair.sr25519.fromUri("//Alice"); // Get chain information final runtimeVersion = await polkadot.rpc.state.getRuntimeVersion(); final currentBlockNumber = (await polkadot.query.system.number()) - 1; final currentBlockHash = await polkadot.query.system.blockHash(currentBlockNumber); final genesisHash = await polkadot.query.system.blockHash(0); final nonce = await polkadot.rpc.system.accountNextIndex(wallet.address); // Get metadata and create merkleizer final metadataBytes = await polkadot.rpc.state.getMetadata(); final input = Input.fromBytes(metadataBytes); final prefixed = RuntimeMetadataPrefixed.codec.decode(input); final merkleizer = MetadataMerkleizer.fromMetadata( prefixed.metadata, decimals: 10, tokenSymbol: 'DOT', ); // Generate metadata hash final metadataHash = hex.encode(merkleizer.digest()); print('Metadata hash: 0x$metadataHash'); // Create the call final multiAddress = $MultiAddress().id(wallet.publicKey.bytes); final transferCall = polkadot.tx.balances.transferKeepAlive( dest: multiAddress, value: BigInt.from(1000000000000), // 1 DOT ); final encodedCall = transferCall.encode(); // Create signing payload WITH metadata hash final payload = SigningPayload( method: encodedCall, specVersion: runtimeVersion.specVersion, transactionVersion: runtimeVersion.transactionVersion, genesisHash: encodeHex(genesisHash), blockHash: encodeHex(currentBlockHash), blockNumber: currentBlockNumber, eraPeriod: 64, nonce: nonce, tip: 0, metadataHash: metadataHash, // ← Metadata hash ).encode(polkadot.registry); // Sign the payload final signature = wallet.sign(payload); // Create extrinsic WITH metadata hash final extrinsic = ExtrinsicPayload( signer: wallet.bytes(), method: encodedCall, signature: signature, eraPeriod: 64, blockNumber: currentBlockNumber, nonce: nonce, tip: 0, metadataHash: metadataHash, // ← Metadata hash ).encode(polkadot.registry, SignatureType.sr25519); // Submit the transaction final author = AuthorApi(provider); await author.submitAndWatchExtrinsic(extrinsic, (data) { print('Status: ${data.type} - ${data.value}'); }); } ``` ## Understanding the Digest Structure The metadata digest (V1 format) contains: ```dart class MetadataDigest { final Uint8List typeInformationTreeRoot; // Merkle root of type tree final Uint8List extrinsicMetadataHash; // Hash of extrinsic metadata final int specVersion; // Runtime spec version final String specName; // Chain name final int base58Prefix; // SS58 address prefix final int decimals; // Token decimals final String tokenSymbol; // Token symbol } ``` The final hash is Blake3(digest). ## The Type Information Tree The merkleizer builds a binary tree from all types used in: - Extrinsic format (address, signature, call types) - Signed extensions (types in extra and additional signed data) Only types actually used are included, making the tree minimal. ## Proof Structure A proof contains: 1. The leaf nodes (actual type definitions used) 2. The leaf indices in the tree 3. The proof nodes (merkle path) 4. Extrinsic metadata 5. Extra information (spec version, name, etc.) This allows verification without the full metadata. ## Verification Flow 1. Client generates metadata hash from local metadata 2. Client includes hash in transaction 3. Chain receives transaction 4. Chain computes its own metadata hash 5. Chain compares hashes 6. If match: transaction proceeds 7. If mismatch: transaction rejected ## Metadata V15 Requirement ::callout{type="caution"} Metadata merkleization requires V15 metadata. It will not work with V14. :: ```dart // Check metadata version if (prefixed.metadata.version != 15) { throw Exception('Metadata merkleization requires V15 metadata'); } ``` ## Performance Considerations Creating the merkleizer is expensive: - It processes all types in the metadata - Builds a complete type tree - Computes merkle hashes ::callout{type="tip"} ### Cache the Merkleizer Create it once per metadata version and reuse it for all transactions. :: ```dart class MetadataHashCache { final Map _cache = {}; MetadataMerkleizer? _merkleizer; String getHash(RuntimeMetadata metadata) { final version = metadata.extrinsic.version; if (_cache.containsKey(version)) { return _cache[version]!; } // Create new merkleizer _merkleizer = MetadataMerkleizer.fromMetadata( metadata, decimals: 10, tokenSymbol: 'DOT', ); final hash = hex.encode(_merkleizer!.digest()); _cache[version] = hash; return hash; } } ``` ## Best Practices ::callout{type="tip"} ### Always Use for Offline Signing If you're signing transactions offline (hardware wallets, air-gapped devices), always include the metadata hash. :: ::callout{type="tip"} ### Verify Chain Support Not all chains support metadata hash yet. Check the signed extensions to see if `CheckMetadataHash` is present. :: ::callout{type="caution"} ### Match Metadata Versions The metadata used to generate the hash MUST match the on-chain metadata version. :: ::callout{type="tip"} ### Cache the Hash Generate the hash once per metadata version and cache it. The hash doesn't change unless the metadata changes. :: ## Checking Chain Support To check if a chain supports metadata hash: ```dart bool supportsMetadataHash(MetadataTypeRegistry registry) { final extensions = registry.signedExtensions; return extensions.any((ext) => ext.identifier == 'CheckMetadataHash'); } // Usage if (supportsMetadataHash(registry)) { print('Chain supports metadata hash'); // Use metadata hash in transactions } else { print('Chain does not support metadata hash'); // Omit metadata hash (set to null) } ``` ## Error Handling ```dart try { final merkleizer = MetadataMerkleizer.fromMetadata( metadata, decimals: 10, tokenSymbol: 'DOT', specVersion: expectedVersion, ); final hash = hex.encode(merkleizer.digest()); } on Exception catch (e) { if (e.toString().contains('mismatch')) { print('Metadata verification failed: $e'); // Metadata doesn't match expected values } else { print('Error generating metadata hash: $e'); } } ``` ## Next Steps - [Complete Example Guide](https://polkadart.dev/guides/use-metadata-hash) - Full transaction example - [ChainInfo API](https://polkadart.dev/substrate-metadata/chain-info) - High-level metadata operations - [Type Registry](https://polkadart.dev/substrate-metadata/type-registry) - Understanding type resolution # Overview ## What is polkadart\_scale\_codec? The `polkadart_scale_codec` package is Polkadart's pure Dart implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) codec. SCALE is the primary encoding format used by Substrate-based blockchains, including Polkadot and Kusama, for efficient binary serialization and deserialization of data. Think of it as the fundamental layer that enables your Dart application to speak the same language as the blockchain - converting complex data structures into compact binary representations and back. ## What is SCALE? SCALE (Simple Concatenated Aggregate Little-Endian) is a lightweight, efficient encoding format with these characteristics: - **Compact**: Minimal overhead and space-efficient encoding - **Deterministic**: Same input always produces same output - **Self-describing**: Type information is known from context (metadata) - **Little-endian**: Consistent byte ordering across platforms SCALE is specifically designed for blockchain use cases where: - Storage space is precious - Network bandwidth is limited - Deterministic encoding is critical for consensus ## Key Features ::card :::card ### Complete Type Coverage Full support for all SCALE primitive and composite types ::: :::card ### Input/Output Abstraction Flexible I/O system supporting bytes, hex, and custom outputs ::: :::card ### Compact Encoding Space-efficient compact integer encoding for optimal storage ::: :::card ### Generic Codecs Support for Option, Result, tuples, arrays, and sequences ::: :::card ### Composite Types Build complex structures from simple codecs ::: :::card ### Zero Dependencies Pure Dart implementation with minimal external dependencies ::: ## Core Concepts ### The Codec Interface Every type in SCALE has a corresponding codec that implements the `Codec` mixin: ```dart mixin Codec { // Decode from binary input E decode(Input input); // Encode to binary output void encodeTo(E value, Output output); // Encode to bytes Uint8List encode(E value); // Hint about encoded size int sizeHint(E value); } ``` ### Input and Output The codec system uses abstracted I/O: ```dart // Create input from various sources final input1 = Input.fromHex('0x2a'); final input2 = Input.fromBytes([42]); // Create different output types final byteOutput = ByteOutput(); final hexOutput = HexOutput(); ``` ## Available Codecs ### Primitive Types | Type | Description | Dart Type | | --------------------- | ----------------------- | --------- | | **bool** | Boolean value | `bool` | | **u8, u16, u32, u64** | Unsigned integers | `int` | | **u128, u256** | Large unsigned integers | `BigInt` | | **i8, i16, i32, i64** | Signed integers | `int` | | **i128, i256** | Large signed integers | `BigInt` | | **str** | UTF-8 string | `String` | ### Compact Types | Type | Description | Use Case | | ----------------- | --------------------------- | ------------------------ | | **Compact** | Compact integer encoding | Space-efficient integers | | **CompactBigInt** | Compact encoding for BigInt | Large numbers | ### Collection Types | Type | Description | Dart Type | | --------------- | ---------------------- | ------------ | | **Array** | Fixed-length array | `List` | | **Sequence** | Variable-length vector | `List` | | **BTreeMap** | Sorted map | `Map` | | **Set** | Unique collection | `Set` | | **BitSequence** | Bit array | `List` | ### Generic Types | Type | Description | Dart Type | | ---------- | ---------------- | ------------------- | | **Option** | Optional value | `T?` or `Option` | | **Result** | Result type | `Result` | | **Tuple** | Fixed-size tuple | `List` | ### Composite Types | Type | Description | Dart Type | | ------------- | ------------- | ---------------------- | | **Composite** | Struct/object | `Map` | | **Enum** | Enumeration | `MapEntry` | ## Installation Add polkadart\_scale\_codec to your `pubspec.yaml`: ```yaml dependencies: polkadart_scale_codec: ^2.0.0 ``` ## Quick Start Here's a simple example encoding and decoding data: ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; void main() { // Encode a simple integer final output = ByteOutput(); U32Codec.codec.encodeTo(42, output); final bytes = output.toBytes(); print('Encoded: ${bytes}'); // [42, 0, 0, 0] // Decode it back final input = Input.fromBytes(bytes); final value = U32Codec.codec.decode(input); print('Decoded: $value'); // 42 // Using hex for readability final hexOutput = HexOutput(); U32Codec.codec.encodeTo(42, hexOutput); print('Hex: ${hexOutput.toString()}'); // 0x2a000000 } ``` ## Common Use Cases ### Encoding Account Balances ```dart // Compact encoding saves space for large numbers final balance = BigInt.from(1000000000000); // 1 DOT final output = ByteOutput(); CompactBigIntCodec.codec.encodeTo(balance, output); ``` ### Working with Optional Values ```dart // Encoding Some(value) final codec = OptionCodec(U32Codec.codec); final output = ByteOutput(); codec.encodeTo(42, output); // [1, 42, 0, 0, 0] // Encoding None codec.encodeTo(null, output); // [0] ``` ### Complex Structures ```dart // Account info structure final accountCodec = CompositeCodec({ 'nonce': U32Codec.codec, 'consumers': U32Codec.codec, 'providers': U32Codec.codec, 'data': CompositeCodec({ 'free': U128Codec.codec, 'reserved': U128Codec.codec, }), }); final accountData = { 'nonce': 5, 'consumers': 0, 'providers': 1, 'data': { 'free': BigInt.from(1000000000000), 'reserved': BigInt.zero, }, }; final encoded = accountCodec.encode(accountData); ``` ## Architecture The package is organized into several modules: ### Core Module - **Codec**: Base interface for all codecs - **Input/Output**: I/O abstraction layer ### Primitives Module - All primitive type codecs (integers, booleans, strings) - Generic codecs (Option, Result, etc.) - Collection codecs (Array, Sequence, etc.) - Composite codecs (Composite, Enum) ### Extended Codecs - Specialized codecs built on top of primitives - Length-prefixed codec for custom use cases ### Utils Module - Exception types - Helper functions - Regular expressions for parsing ## Integration with Other Packages The `polkadart_scale_codec` package is used throughout the Polkadart ecosystem: - **substrate\_metadata**: Uses SCALE codecs to decode metadata and chain data - **polkadart**: Uses codecs for encoding extrinsics and decoding storage - **polkadart\_cli**: Generates type-safe codecs from chain metadata ## Performance Characteristics SCALE encoding is designed for performance: - **Zero-copy decoding**: Direct buffer access where possible - **Compact representation**: Minimal overhead for common values - **Predictable**: Size hints allow pre-allocation - **Efficient**: Little-endian encoding is native on most platforms ## When to Use This Package You'll use `polkadart_scale_codec` when: - Building custom blockchain interactions - Implementing custom storage queries - Creating offline transaction signing - Developing blockchain explorers - Building indexers or monitoring tools - Working with raw blockchain data For most application development, the higher-level `polkadart` and `substrate_metadata` packages handle SCALE encoding automatically. ## Next Steps - [Primitive Types](https://polkadart.dev/scale-codec/primitives) - Learn about basic SCALE types - [Compact Encoding](https://polkadart.dev/scale-codec/compact) - Understand compact integer encoding - [Collections](https://polkadart.dev/scale-codec/collections) - Work with arrays, sequences, and maps - [Generic Types](https://polkadart.dev/scale-codec/generic-types) - Use Option, Result, and tuples - [Composite Types](https://polkadart.dev/scale-codec/composite-types) - Build complex structures - [Input/Output](https://polkadart.dev/scale-codec/input-output) - Master the I/O system :: # Primitive Types ## Primitive Types Overview SCALE primitive types are the building blocks of all blockchain data. The `polkadart_scale_codec` package provides codecs for all SCALE primitive types, enabling you to encode and decode basic values. ## Boolean Type The simplest type - a single byte representing true or false: ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; // Encoding final output = ByteOutput(); BoolCodec.codec.encodeTo(true, output); print(output.toBytes()); // [1] BoolCodec.codec.encodeTo(false, output); print(output.toBytes()); // [1, 0] // Decoding final input = Input.fromBytes([1]); final value = BoolCodec.codec.decode(input); print(value); // true ``` **Encoding Rules:** - `false` β†’ `0x00` - `true` β†’ `0x01` ## Unsigned Integers SCALE provides unsigned integers of various sizes, all encoded in little-endian format: ### U8 - 8-bit Unsigned Integer ```dart final codec = U8Codec.codec; // Encode final output = ByteOutput(); codec.encodeTo(42, output); print(output.toBytes()); // [42] // Decode final input = Input.fromBytes([42]); final value = codec.decode(input); print(value); // 42 ``` **Range:** 0 to 255 **Size:** 1 byte ### U16 - 16-bit Unsigned Integer ```dart final codec = U16Codec.codec; // Encode final output = ByteOutput(); codec.encodeTo(258, output); // 0x0102 in little-endian print(output.toBytes()); // [2, 1] // Decode final input = Input.fromBytes([2, 1]); final value = codec.decode(input); print(value); // 258 ``` **Range:** 0 to 65,535 **Size:** 2 bytes **Encoding:** Little-endian ### U32 - 32-bit Unsigned Integer ```dart final codec = U32Codec.codec; // Encode final output = ByteOutput(); codec.encodeTo(16909060, output); // 0x01020304 print(output.toBytes()); // [4, 3, 2, 1] // Decode final input = Input.fromHex('0x04030201'); final value = codec.decode(input); print(value); // 16909060 ``` **Range:** 0 to 4,294,967,295 **Size:** 4 bytes **Encoding:** Little-endian ### U64 - 64-bit Unsigned Integer ```dart final codec = U64Codec.codec; // Large values final value = 1234567890123456789; final encoded = codec.encode(value); final decoded = codec.decode(Input.fromBytes(encoded)); print(decoded); // 1234567890123456789 ``` **Range:** 0 to 2^64 - 1 **Size:** 8 bytes **Encoding:** Little-endian ### U128 - 128-bit Unsigned Integer For very large numbers, U128 uses `BigInt`: ```dart final codec = U128Codec.codec; // Very large balance final balance = BigInt.parse('340282366920938463463374607431768211455'); final encoded = codec.encode(balance); final decoded = codec.decode(Input.fromBytes(encoded)); print(decoded); // 340282366920938463463374607431768211455 ``` **Range:** 0 to 2^128 - 1 **Size:** 16 bytes **Type:** `BigInt` ### U256 - 256-bit Unsigned Integer For cryptographic values and extremely large numbers: ```dart final codec = U256Codec.codec; // Maximum U256 value final maxValue = BigInt.parse( '115792089237316195423570985008687907853269984665640564039457584007913129639935' ); final output = HexOutput(); codec.encodeTo(maxValue, output); print(output.toString()); // 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ``` **Range:** 0 to 2^256 - 1 **Size:** 32 bytes **Type:** `BigInt` ## Signed Integers Signed integers use two's complement representation: ### I8 - 8-bit Signed Integer ```dart final codec = I8Codec.codec; // Positive value codec.encodeTo(42, ByteOutput()); // [42] // Negative value final output = ByteOutput(); codec.encodeTo(-42, output); print(output.toBytes()); // [214] (two's complement of -42) // Decode final value = codec.decode(Input.fromBytes([214])); print(value); // -42 ``` **Range:** -128 to 127 **Size:** 1 byte ### I16 - 16-bit Signed Integer ```dart final codec = I16Codec.codec; // Encode -1000 final output = ByteOutput(); codec.encodeTo(-1000, output); print(output.toBytes()); // [24, 252] in little-endian // Decode final input = Input.fromBytes([24, 252]); final value = codec.decode(input); print(value); // -1000 ``` **Range:** -32,768 to 32,767 **Size:** 2 bytes ### I32 - 32-bit Signed Integer ```dart final codec = I32Codec.codec; // Large negative number final value = -1234567890; final encoded = codec.encode(value); final decoded = codec.decode(Input.fromBytes(encoded)); print(decoded); // -1234567890 ``` **Range:** -2^31 to 2^31 - 1 **Size:** 4 bytes ### I64 - 64-bit Signed Integer ```dart final codec = I64Codec.codec; // Very large negative final value = -9223372036854775808; // Minimum i64 final encoded = codec.encode(value); ``` **Range:** -2^63 to 2^63 - 1 **Size:** 8 bytes ### I128 - 128-bit Signed Integer ```dart final codec = I128Codec.codec; // Large signed value final value = BigInt.parse('-170141183460469231731687303715884105728'); final encoded = codec.encode(value); final decoded = codec.decode(Input.fromBytes(encoded)); print(decoded); ``` **Range:** -2^127 to 2^127 - 1 **Size:** 16 bytes **Type:** `BigInt` ### I256 - 256-bit Signed Integer ```dart final codec = I256Codec.codec; // Maximum positive I256 final maxValue = BigInt.parse( '57896044618658097711785492504343953926634992332820282019728792003956564819967' ); final output = HexOutput(); codec.encodeTo(maxValue, output); print(output.toString()); // 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f ``` **Range:** -2^255 to 2^255 - 1 **Size:** 32 bytes **Type:** `BigInt` ## String Type UTF-8 encoded strings with compact length prefix: ```dart final codec = StrCodec.codec; // Encode final output = ByteOutput(); codec.encodeTo('Hello', output); // First byte is compact-encoded length (5) // Followed by UTF-8 bytes print(output.toBytes()); // [20, 72, 101, 108, 108, 111] // Decode final input = Input.fromHex('0x1448656c6c6f'); final value = codec.decode(input); print(value); // 'Hello' ``` **Encoding:** 1. Compact-encoded length prefix 2. UTF-8 bytes ### Empty String ```dart final output = ByteOutput(); StrCodec.codec.encodeTo('', output); print(output.toBytes()); // [0] - just the length ``` ### Unicode Strings ```dart final emoji = 'πŸ‘‹πŸŒ'; final encoded = StrCodec.codec.encode(emoji); // Length prefix + UTF-8 bytes of emoji ``` ## Sequences (Vec) Dynamic-length vectors for primitive types: ### U8 Sequence (Vec :u8[)] Most efficient for byte arrays: ```dart final codec = U8SequenceCodec.codec; // Encode final bytes = [1, 2, 3, 4, 5]; final output = ByteOutput(); codec.encodeTo(bytes, output); // [20, 1, 2, 3, 4, 5] - 20 is compact(5) // Decode final input = Input.fromHex('0x14010203040'); final decoded = codec.decode(input); print(decoded); // [1, 2, 3, 4, 5] ``` ### Other Sequences For other types, use `SequenceCodec`: ```dart // Vec final codec = SequenceCodec(U32Codec.codec); final values = [100, 200, 300]; final encoded = codec.encode(values); ``` ## Arrays Fixed-length arrays: ### U8 Array ```dart final codec = U8ArrayCodec(4); // Must be exact length final bytes = [1, 2, 3, 4]; final output = ByteOutput(); codec.encodeTo(bytes, output); // No length prefix - just [1, 2, 3, 4] // Wrong length throws exception try { codec.encodeTo([1, 2, 3], output); // Error! } catch (e) { print('Invalid length'); } ``` ### Generic Arrays ```dart final codec = ArrayCodec(U32Codec.codec, 3); final values = [100, 200, 300]; final encoded = codec.encode(values); ``` ## Complete Example: Account Balance Here's a real-world example encoding account balance information: ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; void main() { // Account has free and reserved balance final free = BigInt.from(1000000000000); // 1 DOT final reserved = BigInt.from(100000000000); // 0.1 DOT // Encode final output = ByteOutput(); U128Codec.codec.encodeTo(free, output); U128Codec.codec.encodeTo(reserved, output); print('Encoded balances: ${output.toBytes()}'); // Decode final input = Input.fromBytes(output.toBytes()); final decodedFree = U128Codec.codec.decode(input); final decodedReserved = U128Codec.codec.decode(input); print('Free: $decodedFree'); print('Reserved: $decodedReserved'); } ``` ## Type Conversion Table | SCALE Type | Dart Type | Size | Codec | | ---------- | ----------- | -------- | ----------------------- | | bool | `bool` | 1 byte | `BoolCodec.codec` | | u8 | `int` | 1 byte | `U8Codec.codec` | | u16 | `int` | 2 bytes | `U16Codec.codec` | | u32 | `int` | 4 bytes | `U32Codec.codec` | | u64 | `int` | 8 bytes | `U64Codec.codec` | | u128 | `BigInt` | 16 bytes | `U128Codec.codec` | | u256 | `BigInt` | 32 bytes | `U256Codec.codec` | | i8 | `int` | 1 byte | `I8Codec.codec` | | i16 | `int` | 2 bytes | `I16Codec.codec` | | i32 | `int` | 4 bytes | `I32Codec.codec` | | i64 | `int` | 8 bytes | `I64Codec.codec` | | i128 | `BigInt` | 16 bytes | `I128Codec.codec` | | i256 | `BigInt` | 32 bytes | `I256Codec.codec` | | String | `String` | Variable | `StrCodec.codec` | | Vec\ | `List` | Variable | `U8SequenceCodec.codec` | | [u8; N] | `List` | N bytes | `U8ArrayCodec(N)` | ## Best Practices ::callout{type="tip"} ### Use Appropriate Integer Sizes Choose the smallest type that fits your data to minimize storage and bandwidth. :: ::callout{type="tip"} ### Little-Endian Awareness SCALE uses little-endian encoding. When working with hex strings, remember byte order is reversed. :: ::callout{type="caution"} ### BigInt for Large Values For values larger than JavaScript's MAX\_SAFE\_INTEGER or Dart's int range, always use BigInt codecs. :: ::callout{type="tip"} ### Prefer U8Array for Fixed Sizes For fixed-length byte arrays (like hashes), use `U8ArrayCodec` instead of `U8SequenceCodec` to save the length prefix byte. :: ## Next Steps - [Compact Encoding](https://polkadart.dev/scale-codec/compact) - Learn space-efficient integer encoding - [Collections](https://polkadart.dev/scale-codec/collections) - Work with sequences and maps - [Generic Types](https://polkadart.dev/scale-codec/generic-types) - Use Option and Result # Compact Encoding ## What is Compact Encoding? Compact encoding is a variable-length integer encoding scheme in SCALE that optimizes storage by using fewer bytes for smaller numbers. This is crucial for blockchain applications where storage space is at a premium. Instead of always using 4 or 8 bytes for every integer, compact encoding uses: - **1 byte** for values 0-63 - **2 bytes** for values 64-16,383 - **4 bytes** for values 16,384-1,073,741,823 - **5+ bytes** for larger values ## Why Use Compact Encoding? In blockchain applications, many values tend to be small: - Transaction counts (nonces) - Collection lengths - Small balances - Indices and counters Compact encoding can save significant space: - Regular u32: `42` β†’ 4 bytes `[42, 0, 0, 0]` - Compact: `42` β†’ 1 byte `[168]` (42 << 2) Over thousands of transactions, this adds up to substantial savings. ## Compact Integer Codec The `CompactCodec` encodes standard Dart `int` values: ### Basic Usage ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; final codec = CompactCodec.codec; // Small value (0-63): 1 byte final output1 = HexOutput(); codec.encodeTo(42, output1); print(output1.toString()); // 0xa8 (42 << 2 = 168 = 0xa8) // Medium value (64-16,383): 2 bytes final output2 = HexOutput(); codec.encodeTo(1000, output2); print(output2.toString()); // 0xa10f // Decode final input = Input.fromHex('0xa8'); final value = codec.decode(input); print(value); // 42 ``` ### Encoding Rules Compact integers use the last 2 bits as a mode indicator: **Mode 00 (Single-byte):** Values 0-63 ```dart codec.encodeTo(0, output); // 0x00 codec.encodeTo(42, output); // 0xa8 (42 << 2 | 0b00) codec.encodeTo(63, output); // 0xfc (63 << 2 | 0b00) ``` **Mode 01 (Two-byte):** Values 64-16,383 ```dart codec.encodeTo(64, output); // 0x0101 codec.encodeTo(1000, output); // 0xa10f codec.encodeTo(16383, output); // 0xfdff ``` **Mode 10 (Four-byte):** Values 16,384-1,073,741,823 ```dart codec.encodeTo(16384, output); // 0x02000100 codec.encodeTo(1000000, output); // 0x02093d00 ``` **Mode 11 (Big integer):** Values β‰₯ 1,073,741,824 ```dart // First byte: ((bytesNeeded - 4) << 2) | 0b11 // Followed by the value in little-endian codec.encodeTo(100000000000, output); ``` ### Size Hints The codec can predict encoded size: ```dart final codec = CompactCodec.codec; print(codec.sizeHint(42)); // 1 print(codec.sizeHint(1000)); // 2 print(codec.sizeHint(100000)); // 4 print(codec.sizeHint(100000000000)); // Variable ``` ## Compact BigInt Codec For values larger than Dart's `int` range, use `CompactBigIntCodec`: ### Basic Usage ```dart final codec = CompactBigIntCodec.codec; // Large value final value = BigInt.from(1234567890123456789); final output = HexOutput(); codec.encodeTo(value, output); // Decode final decoded = codec.decode(Input.fromHex(output.toString())); print(decoded); // 1234567890123456789 ``` ### Working with Token Amounts Blockchain tokens often have large values when represented in smallest unit: ```dart // 1 DOT = 10^10 Plancks final oneDot = BigInt.from(10000000000); final codec = CompactBigIntCodec.codec; final encoded = codec.encode(oneDot); print('Encoding 1 DOT:'); print(' Regular U128: 16 bytes'); print(' Compact: ${encoded.length} bytes'); ``` ### Maximum Values by Size ```dart // 1 byte: 0-63 CompactBigIntCodec.codec.encodeTo(BigInt.from(63), output); // 2 bytes: 64-16,383 CompactBigIntCodec.codec.encodeTo(BigInt.from(16383), output); // 4 bytes: 16,384-1,073,741,823 CompactBigIntCodec.codec.encodeTo(BigInt.from(1073741823), output); // 5+ bytes: 1,073,741,824+ CompactBigIntCodec.codec.encodeTo( BigInt.parse('18446744073709551615'), // u64::MAX output, ); ``` ## When to Use Compact Encoding ### Use Compact When: ::card :::card ### Collection Lengths Vector and sequence lengths are always compact-encoded ```dart final codec = SequenceCodec(U32Codec.codec); // Length is automatically compact-encoded ``` ::: :::card ### Small Integers Values that are usually small (< 16,384) ```dart // Transaction nonce final nonce = 5; CompactCodec.codec.encodeTo(nonce, output); ``` ::: :::card ### Token Amounts Large token amounts in smallest unit ```dart final balance = BigInt.from(1000000000000); CompactBigIntCodec.codec.encodeTo(balance, output); ``` ::: :::card ### Indices Array indices, reference counts, etc. ```dart final index = 42; CompactCodec.codec.encodeTo(index, output); ``` ::: ### Don't Use Compact When: :::callout{type="caution"} Fixed-size encoding is required (e.g., in cryptographic operations or when exact byte size matters) ::: :::callout{type="caution"} Values are consistently large and compact encoding would not save space ::: :::callout{type="caution"} Compatibility with external systems that don't support compact encoding ::: ## Decoding Examples ### Decoding Collection Lengths ```dart // Vec encoding includes compact length final input = Input.fromHex('0x0c64000000c8000000e8030000'); // 0x0c = compact(3) - three elements // Followed by three u32 values // Decode the length final length = CompactCodec.codec.decode(input); print('Length: $length'); // 3 // Decode the elements final elements = List.generate( length, (_) => U32Codec.codec.decode(input), ); print(elements); // [100, 200, 1000] ``` ### Decoding with Unknown Size ```dart void decodeCompact(Input input) { // Read first byte to determine mode final firstByte = input.read(); input.resetOffset(); // Go back final mode = firstByte & 0b11; switch (mode) { case 0b00: print('Single-byte mode'); break; case 0b01: print('Two-byte mode'); break; case 0b10: print('Four-byte mode'); break; case 0b11: final bytes = (firstByte >> 2) + 4; print('Big integer mode: $bytes bytes'); break; } // Now decode normally final value = CompactCodec.codec.decode(input); print('Value: $value'); } ``` ## Complete Example: Transaction Data Real-world example encoding transaction information: ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; class Transaction { final int nonce; final BigInt tip; final List callData; Transaction(this.nonce, this.tip, this.callData); Uint8List encode() { final output = ByteOutput(); // Nonce: usually small, use compact CompactCodec.codec.encodeTo(nonce, output); // Tip: can be large, use compact BigInt CompactBigIntCodec.codec.encodeTo(tip, output); // Call data: length-prefixed bytes CompactCodec.codec.encodeTo(callData.length, output); output.write(callData); return output.toBytes(); } static Transaction decode(Input input) { final nonce = CompactCodec.codec.decode(input); final tip = CompactBigIntCodec.codec.decode(input); final callDataLength = CompactCodec.codec.decode(input); final callData = input.readBytes(callDataLength); return Transaction(nonce, tip, callData); } } void main() { // Create transaction final tx = Transaction( 5, // nonce BigInt.from(1000000000), // tip (0.1 DOT) [1, 2, 3, 4], // call data ); // Encode final encoded = tx.encode(); print('Encoded size: ${encoded.length} bytes'); // Decode final decoded = Transaction.decode(Input.fromBytes(encoded)); print('Nonce: ${decoded.nonce}'); print('Tip: ${decoded.tip}'); print('Call data: ${decoded.callData}'); } ``` ## Space Savings Analysis ### Comparison Table | Value | Regular u32 | Compact | Savings | | --------- | ----------- | ------- | ------- | | 0 | 4 bytes | 1 byte | 75% | | 42 | 4 bytes | 1 byte | 75% | | 63 | 4 bytes | 1 byte | 75% | | 64 | 4 bytes | 2 bytes | 50% | | 1,000 | 4 bytes | 2 bytes | 50% | | 16,383 | 4 bytes | 2 bytes | 50% | | 16,384 | 4 bytes | 4 bytes | 0% | | 1,000,000 | 4 bytes | 4 bytes | 0% | ### Real-World Impact In a typical block: - 1000 transactions with nonces 0-999 - Regular encoding: 4,000 bytes - Compact encoding: \~2,000 bytes - **Savings: \~50%** ## Advanced Topics ### Custom Compact Types You can wrap compact encoding for domain-specific types: ```dart class CompactBalance { final BigInt value; const CompactBalance(this.value); } class CompactBalanceCodec with Codec { const CompactBalanceCodec(); @override void encodeTo(CompactBalance value, Output output) { CompactBigIntCodec.codec.encodeTo(value.value, output); } @override CompactBalance decode(Input input) { return CompactBalance(CompactBigIntCodec.codec.decode(input)); } @override bool isSizeZero() => false; } ``` ### Compact in Composite Types Compact encoding is commonly used within composite structures: ```dart final accountCodec = CompositeCodec({ 'nonce': CompactCodec.codec, // Use compact for nonce 'balance': CompactBigIntCodec.codec, // Compact for balance 'data': U8SequenceCodec.codec, // Sequence has compact length }); ``` ## Common Pitfalls :::callout{type="caution"} ### Don't Mix Compact with Fixed-Size Never mix compact encoding with contexts expecting fixed sizes: ```dart // Wrong: Array of compact values ArrayCodec(CompactCodec.codec, 10); // Don't do this! // Right: Array of regular values ArrayCodec(U32Codec.codec, 10); ``` ::: :::callout{type="caution"} ### Watch for Overflow `CompactCodec` uses `int`, which has limits: ```dart // For very large values, use CompactBigIntCodec final large = BigInt.parse('9999999999999999999'); CompactBigIntCodec.codec.encodeTo(large, output); ``` ::: :::callout{type="tip"} ### Use Size Hints When pre-allocating buffers, use sizeHint: ```dart final value = 1000000; final expectedSize = CompactCodec.codec.sizeHint(value); // Pre-allocate buffer of expectedSize ``` ::: ## Next Steps - [Collections](https://polkadart.dev/scale-codec/collections) - Learn about sequences and arrays - [Generic Types](https://polkadart.dev/scale-codec/generic-types) - Explore Option and Result types - [Input/Output](https://polkadart.dev/scale-codec/input-output) - Master the I/O system :: # Collections ## Collection Types Overview SCALE provides several collection types for organizing multiple values. Each has different characteristics and use cases: - **Sequence** (Vec): Variable-length, dynamically-sized - **Array**: Fixed-length, compile-time size - **BTreeMap**: Sorted key-value pairs - **Set**: Unique values - **BitSequence**: Compact boolean array ## Sequences (Vec) Sequences are the most common collection type - variable-length vectors with a compact-encoded length prefix. ### Basic Sequence ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; // Vec final codec = SequenceCodec(U32Codec.codec); // Encode final values = [100, 200, 300]; final output = ByteOutput(); codec.encodeTo(values, output); // [12, 100, 0, 0, 0, 200, 0, 0, 0, 44, 1, 0, 0] // 12 = compact(3), then three u32 values // Decode final decoded = codec.decode(Input.fromBytes(output.toBytes())); print(decoded); // [100, 200, 300] ``` ### Encoding Structure A sequence is encoded as: 1. **Length prefix** (compact-encoded) 2. **Elements** (each encoded with inner codec) ```dart // [1, 2, 3] encoded as Vec: // 0x0c010203 // β””β”¬β”˜ β””β”€β”¬β”€β”€β”˜ // β”‚ └── Elements // └─────── Compact(3) ``` ### Nested Sequences Sequences can contain other sequences: ```dart // Vec> final innerCodec = SequenceCodec(U32Codec.codec); final outerCodec = SequenceCodec(innerCodec); final data = [ [1, 2, 3], [4, 5], [6, 7, 8, 9], ]; final encoded = outerCodec.encode(data); ``` ### Sequences of Structs ```dart // Vec<{id: u32, active: bool}> final structCodec = CompositeCodec({ 'id': U32Codec.codec, 'active': BoolCodec.codec, }); final codec = SequenceCodec(structCodec); final data = [ {'id': 1, 'active': true}, {'id': 2, 'active': false}, {'id': 3, 'active': true}, ]; final encoded = codec.encode(data); ``` ### Empty Sequences ```dart final codec = SequenceCodec(U32Codec.codec); // Empty vector final empty = []; final output = ByteOutput(); codec.encodeTo(empty, output); print(output.toBytes()); // [0] - just compact(0) ``` ## Arrays Arrays have a fixed length known at compile time. No length prefix is encoded. ### Basic Arrays ```dart // [u32; 3] - array of exactly 3 u32 values final codec = ArrayCodec(U32Codec.codec, 3); // Encode final values = [100, 200, 300]; final output = ByteOutput(); codec.encodeTo(values, output); // [100, 0, 0, 0, 200, 0, 0, 0, 44, 1, 0, 0] // No length prefix! // Must be exact length try { codec.encodeTo([100, 200], output); // Error! } catch (e) { print('Invalid list length'); } ``` ### Byte Arrays For byte arrays, there's an optimized codec: ```dart // [u8; 32] - common for hashes, keys final codec = U8ArrayCodec(32); final hash = List.generate(32, (i) => i); final encoded = codec.encode(hash); print(encoded.length); // Exactly 32 bytes ``` ### When to Use Arrays vs Sequences | Use Array When | Use Sequence When | | ------------------------ | ---------------------- | | Size is fixed and known | Size varies at runtime | | Save 1 byte (no length) | Dynamic collections | | Type safety requires it | Flexibility is needed | | Hash outputs, signatures | Lists, vectors | ### Common Array Sizes ```dart // Common in blockchain final hash256 = U8ArrayCodec(32); // SHA-256, Blake2b-256 final hash512 = U8ArrayCodec(64); // SHA-512, Blake2b-512 final signature = U8ArrayCodec(64); // ED25519, SR25519 final publicKey = U8ArrayCodec(32); // Account public keys ``` ## Tuples Tuples are fixed-length collections of potentially different types: ### Basic Tuples ```dart // (u32, bool, String) final codec = TupleCodec([ U32Codec.codec, BoolCodec.codec, StrCodec.codec, ]); final value = [42, true, 'hello']; final output = ByteOutput(); codec.encodeTo(value, output); final decoded = codec.decode(Input.fromBytes(output.toBytes())); print(decoded); // [42, true, 'hello'] ``` ### Tuple Structure Tuples encode each element in order with no separator: ```dart // (u8, u16) with values (5, 1000) // Encoded as: [5, 232, 3] // β””β”˜ β””β”€β”€β”¬β”€β”€β”˜ // u8 u16 (little-endian) ``` ### Nested Tuples ```dart // ((u32, bool), String) final innerTuple = TupleCodec([U32Codec.codec, BoolCodec.codec]); final outerTuple = TupleCodec([innerTuple, StrCodec.codec]); final value = [ [42, true], 'test', ]; final encoded = outerTuple.encode(value); ``` ### Tuple vs Array | Tuple | Array | | --------------------- | ---------------------- | | Different types | Same type | | `[u32, bool, String]` | `[u32; 3]` | | `TupleCodec([...])` | `ArrayCodec(codec, N)` | | Heterogeneous | Homogeneous | ## BTreeMap Sorted map with key-value pairs: ### Basic BTreeMap ```dart // BTreeMap final codec = BTreeMapCodec( keyCodec: U32Codec.codec, valueCodec: BoolCodec.codec, ); final map = { 100: true, 200: false, 300: true, }; final output = ByteOutput(); codec.encodeTo(map, output); final decoded = codec.decode(Input.fromBytes(output.toBytes())); print(decoded); // {100: true, 200: false, 300: true} ``` ### Encoding Structure BTreeMap is encoded as: 1. **Count** (compact-encoded number of entries) 2. **Key-value pairs** (sorted by key) ```dart // {2: true, 1: false} // Sorted order: {1: false, 2: true} // Encoded: [8, 1, 0, 0, 0, 0, 2, 0, 0, 0, 1] // β””β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ // compact(2) entry 1 entry 2 ``` ### Nested Maps ```dart // BTreeMap> final innerMap = BTreeMapCodec( keyCodec: U32Codec.codec, valueCodec: BoolCodec.codec, ); final outerMap = BTreeMapCodec( keyCodec: U32Codec.codec, valueCodec: innerMap, ); final data = { 1: {10: true, 20: false}, 2: {30: true}, }; final encoded = outerMap.encode(data); ``` ### Complex Map Example ```dart // BTreeMap> final codec = BTreeMapCodec( keyCodec: StrCodec.codec, valueCodec: SequenceCodec(U32Codec.codec), ); final data = { 'alice': [100, 200], 'bob': [300, 400, 500], }; final encoded = codec.encode(data); ``` ## Set Unordered collection of unique values: ### Basic Set ```dart // BTreeSet (implemented as BTreeMap) final codec = SetCodec(U32Codec.codec); final set = {1, 2, 3, 2, 1}; // Duplicates removed final output = ByteOutput(); codec.encodeTo(set, output); final decoded = codec.decode(Input.fromBytes(output.toBytes())); print(decoded); // {1, 2, 3} ``` ### Set Encoding Sets are encoded similarly to sequences: 1. **Count** (compact-encoded) 2. **Elements** (sorted and unique) ```dart // {3, 1, 2} becomes {1, 2, 3} // Encoded: [12, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0] // β””β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ // compact(3) sorted elements ``` ## BitSequence Compact representation of boolean arrays: ### Basic BitSequence ```dart final codec = BitSequenceCodec(); // Boolean array final bits = [true, false, true, true, false, false, true, false]; final encoded = codec.encode(bits); // Decodes back to original final decoded = codec.decode(Input.fromBytes(encoded)); print(decoded); // [true, false, true, true, false, false, true, false] ``` ### Encoding Structure BitSequence packs 8 bools into 1 byte: ```dart // [true, false, true, true, false, false, true, false] // Packed as: 01101001 (reversed bit order) // Plus compact length prefix ``` ### When to Use BitSequence ::card :::card ### Large Boolean Arrays 1000 bools = 125 bytes (vs 1000 bytes) ::: :::card ### Flags and Permissions User permission bitfields ::: :::card ### Sparse Matrices Boolean sparse matrices ::: :::card ### Bloom Filters Compact probabilistic data structures ::: ## Complete Example: Product Catalog Real-world example using multiple collection types: ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; class Product { final int id; final String name; final BigInt price; final List tags; final bool inStock; Product(this.id, this.name, this.price, this.tags, this.inStock); } class ProductCodec with Codec { const ProductCodec(); @override void encodeTo(Product value, Output output) { U32Codec.codec.encodeTo(value.id, output); StrCodec.codec.encodeTo(value.name, output); CompactBigIntCodec.codec.encodeTo(value.price, output); SequenceCodec(StrCodec.codec).encodeTo(value.tags, output); BoolCodec.codec.encodeTo(value.inStock, output); } @override Product decode(Input input) { return Product( U32Codec.codec.decode(input), StrCodec.codec.decode(input), CompactBigIntCodec.codec.decode(input), SequenceCodec(StrCodec.codec).decode(input), BoolCodec.codec.decode(input), ); } @override bool isSizeZero() => false; } void main() { // Catalog: Map> final catalogCodec = BTreeMapCodec( keyCodec: StrCodec.codec, valueCodec: SequenceCodec(ProductCodec()), ); final catalog = { 'electronics': [ Product(1, 'Laptop', BigInt.from(999), ['tech', 'portable'], true), Product(2, 'Phone', BigInt.from(699), ['tech', 'mobile'], true), ], 'books': [ Product(3, 'SCALE Guide', BigInt.from(29), ['tech', 'education'], false), ], }; // Encode final encoded = catalogCodec.encode(catalog); print('Encoded size: ${encoded.length} bytes'); // Decode final decoded = catalogCodec.decode(Input.fromBytes(encoded)); print('Categories: ${decoded.keys.length}'); } ``` ## Performance Considerations ### Size Comparison | Type | Example | Regular | Optimized | Savings | | -------- | --------- | -------- | --------- | ------------- | | Vec\ | [1,2,3] | 13 bytes | 4 bytes | 69% | | BitSeq | 8 bools | 8 bytes | 2 bytes | 75% | | [u8; 32] | Hash | 33 bytes | 32 bytes | 3% | | BTreeMap | 3 entries | Variable | Sorted | Better lookup | ### Best Practices :::callout{type="tip"} ### Choose the Right Collection - Use **Array** for fixed-size data - Use **Sequence** for dynamic lists - Use **BTreeMap** for key-value lookups - Use **BitSequence** for large boolean arrays ::: :::callout{type="tip"} ### Pre-allocate When Possible ```dart final codec = SequenceCodec(U32Codec.codec); final values = List.generate(1000, (i) => i); final expectedSize = codec.sizeHint(values); // Pre-allocate buffer if needed ``` ::: :::callout{type="caution"} ### Avoid Deep Nesting Deeply nested collections can be slow to encode/decode: ```dart // Avoid: Vec>>> // Prefer: Flatten when possible ``` ::: :::callout{type="tip"} ### Use Byte Arrays for Raw Data ```dart // Prefer U8ArrayCodec for fixed-size raw bytes final codec = U8ArrayCodec(32); // Over SequenceCodec for known sizes ``` ::: ## Next Steps - [Generic Types](https://polkadart.dev/scale-codec/generic-types) - Learn about Option and Result - [Composite Types](https://polkadart.dev/scale-codec/composite-types) - Build complex structures - [Input/Output](https://polkadart.dev/scale-codec/input-output) - Master the I/O system :: # Generic Types ## Generic Types Overview SCALE provides generic wrapper types that add semantics to values. These types are essential for representing optional values, error handling, and nullable data in a type-safe way. ## Option Type The `Option` type represents a value that may or may not be present - similar to nullable types but with explicit SCALE encoding. ### Basic Option Usage ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; // Option final codec = OptionCodec(U32Codec.codec); // Encoding Some(value) final output1 = ByteOutput(); codec.encodeTo(42, output1); print(output1.toBytes()); // [1, 42, 0, 0, 0] // β””β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ // Some value // Encoding None final output2 = ByteOutput(); codec.encodeTo(null, output2); print(output2.toBytes()); // [0] // β””β”˜ // None ``` ### Encoding Rules Option is encoded as: - **None**: Single byte `0x00` - **Some(value)**: Byte `0x01` followed by encoded value ```dart final codec = OptionCodec(U32Codec.codec); // None codec.encodeTo(null, output); // [0] // Some(100) codec.encodeTo(100, output); // [1, 100, 0, 0, 0] // Some(0) - note: different from None! codec.encodeTo(0, output); // [1, 0, 0, 0, 0] ``` ### Decoding Options ```dart final codec = OptionCodec(U32Codec.codec); // Decode Some final input1 = Input.fromHex('0x0164000000'); final value1 = codec.decode(input1); print(value1); // 100 // Decode None final input2 = Input.fromHex('0x00'); final value2 = codec.decode(input2); print(value2); // null ``` ### Option with Complex Types ```dart // Option<(u32, bool)> final tupleCodec = TupleCodec([U32Codec.codec, BoolCodec.codec]); final optionCodec = OptionCodec(tupleCodec); // Some final output = ByteOutput(); optionCodec.encodeTo([42, true], output); // [1, 42, 0, 0, 0, 1] // None optionCodec.encodeTo(null, output); // [0] ``` ### Option Class The package also provides an `Option` class for more explicit handling: ```dart // Using Option class final some = Option.some(42); final none = Option.none(); print(some.isSome); // true print(some.isNone); // false print(some.value); // 42 print(none.isSome); // false print(none.isNone); // true ``` ### Nested Options For nested options, use `NestedOptionCodec`: ```dart // Option> final codec = NestedOptionCodec(OptionCodec(U32Codec.codec)); // None codec.encodeTo(Option.none(), output); // [0] // Some(None) codec.encodeTo(Option.some(null), output); // [1, 0] // Some(Some(42)) codec.encodeTo(Option.some(42), output); // [1, 1, 42, 0, 0, 0] ``` ## Result Type The `Result` type represents either success (Ok) or failure (Err) - similar to Rust's Result or functional programming's Either. ### Basic Result Usage ```dart // Result final codec = ResultCodec( U32Codec.codec, // Ok type StrCodec.codec, // Err type ); // Encoding Ok(value) final output1 = ByteOutput(); codec.encodeTo(Result.ok(42), output1); print(output1.toBytes()); // [0, 42, 0, 0, 0] // β””β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ // Ok value // Encoding Err(error) final output2 = ByteOutput(); codec.encodeTo(Result.err('error'), output2); print(output2.toBytes()); // [1, 20, 101, 114, 114, 111, 114] // β””β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ // Err "error" string ``` ### Encoding Rules Result is encoded as: - **Ok(value)**: Byte `0x00` followed by encoded value - **Err(error)**: Byte `0x01` followed by encoded error ### Decoding Results ```dart final codec = ResultCodec(U32Codec.codec, StrCodec.codec); // Decode Ok final input1 = Input.fromHex('0x0064000000'); final result1 = codec.decode(input1); print(result1.isOk); // true print(result1.okValue); // 100 // Decode Err final input2 = Input.fromHex('0x01146661696c6564'); final result2 = codec.decode(input2); print(result2.isErr); // true print(result2.errValue); // "failed" ``` ### Result Class The `Result` class provides type-safe access: ```dart // Create results final success = Result.ok(42); final failure = Result.err('Something went wrong'); // Check status if (success.isOk) { print('Value: ${success.okValue}'); } if (failure.isErr) { print('Error: ${failure.errValue}'); } // Convert to JSON print(success.toJson()); // {Ok: 42} print(failure.toJson()); // {Err: "Something went wrong"} ``` ### Complex Result Types ```dart // Result<{id: u32, name: String}, {code: u8, message: String}> final okCodec = CompositeCodec({ 'id': U32Codec.codec, 'name': StrCodec.codec, }); final errCodec = CompositeCodec({ 'code': U8Codec.codec, 'message': StrCodec.codec, }); final codec = ResultCodec(okCodec, errCodec); // Success case final success = Result.ok({ 'id': 1, 'name': 'Alice', }); // Error case final error = Result.err({ 'code': 404, 'message': 'Not found', }); final encoded = codec.encode(success); ``` ## Common Patterns ### Optional Fields in Structs ```dart // User with optional email final userCodec = CompositeCodec({ 'id': U32Codec.codec, 'name': StrCodec.codec, 'email': OptionCodec(StrCodec.codec), 'age': OptionCodec(U8Codec.codec), }); final user1 = { 'id': 1, 'name': 'Alice', 'email': 'alice@example.com', 'age': 30, }; final user2 = { 'id': 2, 'name': 'Bob', 'email': null, // No email 'age': null, // Age not provided }; final encoded1 = userCodec.encode(user1); final encoded2 = userCodec.encode(user2); ``` ### API Response Pattern ```dart // API responses as Result class ApiResponse { static final codec = ResultCodec( // Success: {data: Vec} CompositeCodec({ 'data': U8SequenceCodec.codec, }), // Error: {code: u16, message: String} CompositeCodec({ 'code': U16Codec.codec, 'message': StrCodec.codec, }), ); } void handleResponse(Uint8List encoded) { final result = ApiResponse.codec.decode(Input.fromBytes(encoded)); if (result.isOk) { final data = result.okValue!['data']; print('Success: $data'); } else { final code = result.errValue!['code']; final message = result.errValue!['message']; print('Error $code: $message'); } } ``` ### Nullable Collections ```dart // Option> final codec = OptionCodec(SequenceCodec(U32Codec.codec)); // Some list codec.encodeTo([1, 2, 3], output); // Empty list (different from None!) codec.encodeTo([], output); // [1, 0] - Some([]) // None codec.encodeTo(null, output); // [0] ``` ## Complete Example: Database Record ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; class DatabaseRecord { final int id; final String name; final int? age; // Optional final String? email; // Optional final Result status; // Ok or Err DatabaseRecord(this.id, this.name, this.age, this.email, this.status); } class DatabaseRecordCodec with Codec { const DatabaseRecordCodec(); @override void encodeTo(DatabaseRecord value, Output output) { U32Codec.codec.encodeTo(value.id, output); StrCodec.codec.encodeTo(value.name, output); OptionCodec(U8Codec.codec).encodeTo(value.age, output); OptionCodec(StrCodec.codec).encodeTo(value.email, output); ResultCodec(StrCodec.codec, StrCodec.codec).encodeTo(value.status, output); } @override DatabaseRecord decode(Input input) { return DatabaseRecord( U32Codec.codec.decode(input), StrCodec.codec.decode(input), OptionCodec(U8Codec.codec).decode(input), OptionCodec(StrCodec.codec).decode(input), ResultCodec(StrCodec.codec, StrCodec.codec).decode(input), ); } @override bool isSizeZero() => false; } void main() { // Active user with all fields final record1 = DatabaseRecord( 1, 'Alice', 30, 'alice@example.com', Result.ok('Active'), ); // Inactive user with missing fields final record2 = DatabaseRecord( 2, 'Bob', null, // Age not provided null, // Email not provided Result.err('Account suspended'), ); final codec = DatabaseRecordCodec(); // Encode both final encoded1 = codec.encode(record1); final encoded2 = codec.encode(record2); print('Record 1 size: ${encoded1.length} bytes'); print('Record 2 size: ${encoded2.length} bytes'); // Decode and verify final decoded1 = codec.decode(Input.fromBytes(encoded1)); print('Name: ${decoded1.name}, Age: ${decoded1.age}'); if (decoded1.status.isOk) { print('Status: ${decoded1.status.okValue}'); } } ``` ## When to Use What ### Use Option When: ::card :::card ### Optional Fields Fields that may not always be present ```dart final codec = CompositeCodec({ 'value': OptionCodec(U32Codec.codec), }); ``` ::: :::card ### Nullable Values Values that can legitimately be null ```dart final codec = OptionCodec(StrCodec.codec); ``` ::: :::card ### Sparse Data Arrays where most elements are None ```dart final codec = SequenceCodec(OptionCodec(U32Codec.codec)); ``` ::: ### Use Result When: :::card ::::card ### Error Handling Operations that can succeed or fail ```dart final codec = ResultCodec(DataCodec(), ErrorCodec()); ``` :::: ::::card ### API Responses Network responses with success/error states ```dart final codec = ResultCodec(SuccessCodec(), ErrorCodec()); ``` :::: ::::card ### Validation Validated vs invalid data ```dart final codec = ResultCodec(ValidCodec(), ValidationErrorCodec()); ``` :::: ## Size Implications ### Option Size | Value | Encoding | Size | | -------- | ------------------ | ------------- | | None | `[0]` | 1 byte | | Some(0) | `[1, 0, 0, 0, 0]` | 5 bytes (u32) | | Some(42) | `[1, 42, 0, 0, 0]` | 5 bytes (u32) | **Rule:** 1 byte overhead for the tag ### Result Size | Value | Encoding | Size | | ----------- | ---------------------------- | ------- | | Ok(42) | `[0, 42, 0, 0, 0]` | 5 bytes | | Err("fail") | `[1, 16, 102, 97, 105, 108]` | 6 bytes | **Rule:** 1 byte overhead for the tag ## Best Practices ::::callout{type="tip"} ### Use Dart Nullables for Option For simple cases, `OptionCodec` works with Dart's nullable types: ```dart final codec = OptionCodec(U32Codec.codec); int? value = 42; // Can be null codec.encodeTo(value, output); ``` :::: ::: :: ::callout{type="tip"} ### Use Result for Explicit Error Handling Result provides better error context than throwing exceptions: ```dart // Good Result validate(UserInput input); // Less good User validate(UserInput input); // throws on error ``` :: ::callout{type="caution"} ### Don't Confuse Option with Default Values ```dart // Option: None vs Some(0) are different! OptionCodec(U32Codec.codec).encodeTo(null, output); // [0] OptionCodec(U32Codec.codec).encodeTo(0, output); // [1, 0, 0, 0, 0] ``` :: ::callout{type="tip"} ### Nested Options Need Special Handling ```dart // For Option>, use NestedOptionCodec final codec = NestedOptionCodec(OptionCodec(U32Codec.codec)); ``` :: ## Next Steps - [Composite Types](https://polkadart.dev/scale-codec/composite-types) - Build complex structures - [Input/Output](https://polkadart.dev/scale-codec/input-output) - Master the I/O system - [Best Practices](https://polkadart.dev/scale-codec/best-practices) - Learn encoding best practices # Composite Types ## Composite Types Overview Composite types allow you to build complex data structures from primitive codecs. They're similar to structs or classes in programming languages, enabling you to encode and decode multi-field objects. ## Composite Codec The `CompositeCodec` encodes and decodes structures (objects with named fields). ### Basic Composite ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; // Define a structure: {id: u32, active: bool, name: String} final codec = CompositeCodec({ 'id': U32Codec.codec, 'active': BoolCodec.codec, 'name': StrCodec.codec, }); // Encode final data = { 'id': 42, 'active': true, 'name': 'Alice', }; final output = ByteOutput(); codec.encodeTo(data, output); // Decode final decoded = codec.decode(Input.fromBytes(output.toBytes())); print(decoded); // {id: 42, active: true, name: Alice} ``` ### Encoding Order Fields are encoded in the order they appear in the codec definition: ```dart final codec = CompositeCodec({ 'field1': U32Codec.codec, // Encoded first 'field2': BoolCodec.codec, // Encoded second 'field3': StrCodec.codec, // Encoded third }); // Encoding of {field1: 100, field2: true, field3: "hi"} // [100, 0, 0, 0, 1, 8, 104, 105] // β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ // field1 field2 field3 ``` ### Nested Composites Composites can contain other composites: ```dart // Address structure final addressCodec = CompositeCodec({ 'street': StrCodec.codec, 'city': StrCodec.codec, 'zipCode': U32Codec.codec, }); // Person structure with nested address final personCodec = CompositeCodec({ 'id': U32Codec.codec, 'name': StrCodec.codec, 'address': addressCodec, // Nested composite }); final person = { 'id': 1, 'name': 'Alice', 'address': { 'street': '123 Main St', 'city': 'Springfield', 'zipCode': 12345, }, }; final encoded = personCodec.encode(person); ``` ### Empty Composites An empty composite encodes to zero bytes: ```dart final emptyCodec = CompositeCodec({}); final output = ByteOutput(); emptyCodec.encodeTo({}, output); print(output.toBytes()); // [] - empty! ``` ### Composite with Collections ```dart // User with tags (Vec) final userCodec = CompositeCodec({ 'id': U32Codec.codec, 'name': StrCodec.codec, 'tags': SequenceCodec(StrCodec.codec), }); final user = { 'id': 1, 'name': 'Alice', 'tags': ['admin', 'verified', 'premium'], }; final encoded = userCodec.encode(user); ``` ## Enum Codecs SCALE supports two types of enums: simple (unit variants) and complex (variants with data). ### Simple Enum Simple enums have variants without associated data: ```dart // enum Status { Active, Inactive, Pending } final codec = SimpleEnumCodec.fromList([ 'Active', // Index 0 'Inactive', // Index 1 'Pending', // Index 2 ]); // Encode final output = ByteOutput(); codec.encodeTo('Active', output); print(output.toBytes()); // [0] codec.encodeTo('Pending', output); print(output.toBytes()); // [0, 2] // Decode final decoded = codec.decode(Input.fromBytes([1])); print(decoded); // 'Inactive' ``` ### Sparse Simple Enum For enums with gaps in indices: ```dart // enum Status { // Active = 0, // Pending = 2, // Skip 1 // Archived = 5, // Skip 3, 4 // } final codec = SimpleEnumCodec.sparse({ 0: 'Active', 2: 'Pending', 5: 'Archived', }); // Encoding codec.encodeTo('Pending', output); // [2] codec.encodeTo('Archived', output); // [5] ``` ### Complex Enum Complex enums have variants with associated data: ```dart // enum Message { // Text(String), // Number(u32), // Tuple(u32, bool), // } final codec = ComplexEnumCodec.fromList([ MapEntry('Text', StrCodec.codec), MapEntry('Number', U32Codec.codec), MapEntry('Tuple', TupleCodec([U32Codec.codec, BoolCodec.codec])), ]); // Encode Text variant final output1 = ByteOutput(); codec.encodeTo(MapEntry('Text', 'Hello'), output1); // [0, 20, 72, 101, 108, 108, 111] // β””β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ // idx "Hello" string // Encode Number variant final output2 = ByteOutput(); codec.encodeTo(MapEntry('Number', 42), output2); // [1, 42, 0, 0, 0] // β””β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ // idx u32 value // Encode Tuple variant final output3 = ByteOutput(); codec.encodeTo(MapEntry('Tuple', [100, true]), output3); // [2, 100, 0, 0, 0, 1] // β””β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ // idx tuple data ``` ### Decoding Complex Enums ```dart final input = Input.fromHex('0x0114426f62'); final decoded = codec.decode(input); print(decoded.key); // 'Number' print(decoded.value); // 42 ``` ### Sparse Complex Enum For enums with non-sequential indices: ```dart // enum Event { // Created(u32) = 0, // Updated(String) = 5, // Deleted = 10, // } final codec = ComplexEnumCodec.sparse({ 0: MapEntry('Created', U32Codec.codec), 5: MapEntry('Updated', StrCodec.codec), 10: MapEntry('Deleted', NullCodec()), }); ``` ## Real-World Examples ### Account Information ```dart // AccountInfo structure from Substrate final accountInfoCodec = CompositeCodec({ 'nonce': U32Codec.codec, 'consumers': U32Codec.codec, 'providers': U32Codec.codec, 'sufficients': U32Codec.codec, 'data': CompositeCodec({ 'free': U128Codec.codec, 'reserved': U128Codec.codec, 'frozen': U128Codec.codec, 'flags': U128Codec.codec, }), }); final accountInfo = { 'nonce': 5, 'consumers': 0, 'providers': 1, 'sufficients': 0, 'data': { 'free': BigInt.from(1000000000000), 'reserved': BigInt.zero, 'frozen': BigInt.zero, 'flags': BigInt.zero, }, }; final encoded = accountInfoCodec.encode(accountInfo); ``` ### Transaction Call ```dart // Transfer call: balances.transfer(dest, amount) final transferCodec = CompositeCodec({ 'dest': U8ArrayCodec(32), // AccountId32 'value': CompactBigIntCodec.codec, }); final call = { 'dest': List.generate(32, (i) => i), // Destination account 'value': BigInt.from(1000000000000), // 1 DOT }; final encoded = transferCodec.encode(call); ``` ### Event Record ```dart // Event with different types final eventCodec = ComplexEnumCodec.fromList([ MapEntry('Transfer', CompositeCodec({ 'from': U8ArrayCodec(32), 'to': U8ArrayCodec(32), 'amount': U128Codec.codec, })), MapEntry('Deposit', CompositeCodec({ 'who': U8ArrayCodec(32), 'amount': U128Codec.codec, })), MapEntry('Withdraw', CompositeCodec({ 'who': U8ArrayCodec(32), 'amount': U128Codec.codec, })), ]); // Transfer event final transferEvent = MapEntry('Transfer', { 'from': List.filled(32, 1), 'to': List.filled(32, 2), 'amount': BigInt.from(1000000000000), }); final encoded = eventCodec.encode(transferEvent); ``` ## Complete Example: Blockchain Data ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; // Define a blockchain extrinsic structure class Extrinsic { final int version; final ExtrinsicSignature? signature; final Call call; Extrinsic(this.version, this.signature, this.call); } class ExtrinsicSignature { final List address; final List signature; final int nonce; final BigInt tip; ExtrinsicSignature(this.address, this.signature, this.nonce, this.tip); } class Call { final String pallet; final String method; final Map args; Call(this.pallet, this.method, this.args); } // Codec for signature final signatureCodec = CompositeCodec({ 'address': U8ArrayCodec(32), 'signature': U8ArrayCodec(64), 'nonce': CompactCodec.codec, 'tip': CompactBigIntCodec.codec, }); // Codec for call arguments (varies by call) final transferArgsCodec = CompositeCodec({ 'dest': U8ArrayCodec(32), 'value': CompactBigIntCodec.codec, }); // Codec for call final callCodec = ComplexEnumCodec.fromList([ MapEntry('Balances.transfer', transferArgsCodec), // Add more call types as needed ]); // Codec for complete extrinsic final extrinsicCodec = CompositeCodec({ 'version': U8Codec.codec, 'signature': OptionCodec(signatureCodec), 'call': callCodec, }); void main() { // Create a signed transfer extrinsic final extrinsic = { 'version': 4, 'signature': { 'address': List.filled(32, 1), 'signature': List.filled(64, 2), 'nonce': 5, 'tip': BigInt.zero, }, 'call': MapEntry('Balances.transfer', { 'dest': List.filled(32, 3), 'value': BigInt.from(1000000000000), }), }; // Encode final encoded = extrinsicCodec.encode(extrinsic); print('Encoded size: ${encoded.length} bytes'); // Decode final decoded = extrinsicCodec.decode(Input.fromBytes(encoded)); print('Version: ${decoded['version']}'); print('Has signature: ${decoded['signature'] != null}'); } ``` ## Design Patterns ### Builder Pattern ```dart class PersonBuilder { static final _codec = CompositeCodec({ 'id': U32Codec.codec, 'name': StrCodec.codec, 'age': U8Codec.codec, 'email': OptionCodec(StrCodec.codec), }); int? id; String? name; int? age; String? email; PersonBuilder setId(int id) { this.id = id; return this; } PersonBuilder setName(String name) { this.name = name; return this; } PersonBuilder setAge(int age) { this.age = age; return this; } PersonBuilder setEmail(String? email) { this.email = email; return this; } Uint8List build() { return _codec.encode({ 'id': id!, 'name': name!, 'age': age!, 'email': email, }); } } // Usage final encoded = PersonBuilder() .setId(1) .setName('Alice') .setAge(30) .setEmail('alice@example.com') .build(); ``` ### Type-Safe Wrappers ```dart class AccountId { final List bytes; const AccountId(this.bytes); } class AccountIdCodec with Codec { final _inner = U8ArrayCodec(32); const AccountIdCodec(); @override void encodeTo(AccountId value, Output output) { _inner.encodeTo(value.bytes, output); } @override AccountId decode(Input input) { return AccountId(_inner.decode(input)); } @override bool isSizeZero() => false; } // Usage with composites final transferCodec = CompositeCodec({ 'from': AccountIdCodec(), 'to': AccountIdCodec(), 'amount': U128Codec.codec, }); ``` ## Best Practices ::callout{type="tip"} ### Maintain Field Order Always maintain the same field order in your codec definition as the data structure: ```dart // Good final codec = CompositeCodec({ 'id': U32Codec.codec, 'name': StrCodec.codec, }); // Order matters for encoding/decoding ``` :: ::callout{type="tip"} ### Use Descriptive Names Use clear, descriptive names for enum variants and struct fields: ```dart // Good ComplexEnumCodec.fromList([ MapEntry('UserCreated', userCodec), MapEntry('UserUpdated', userCodec), MapEntry('UserDeleted', idCodec), ]); // Avoid ComplexEnumCodec.fromList([ MapEntry('A', userCodec), MapEntry('B', userCodec), MapEntry('C', idCodec), ]); ``` :: ::callout{type="caution"} ### Don't Skip Validation Always validate data before encoding: ```dart void encodeUser(Map user) { if (!user.containsKey('id')) { throw Exception('Missing required field: id'); } userCodec.encodeTo(user, output); } ``` :: ::callout{type="tip"} ### Cache Complex Codecs Create complex codecs once and reuse: ```dart // Good class Codecs { static final account = CompositeCodec({...}); static final transfer = ComplexEnumCodec.fromList([...]); } // Avoid recreating on every use ``` :: ## Size Considerations ### Composite Size Composite size is the sum of all fields: ```dart // {id: u32, active: bool, name: String} // With data: {id: 42, active: true, name: "Alice"} // Size: 4 + 1 + (1 + 5) = 11 bytes // β””β”€β”˜ β””β”˜ β””β”€β”€β”¬β”€β”€β”˜ // u32 bool compact(5) + "Alice" ``` ### Enum Size Enum size is 1 byte (index) + variant data: ```dart // enum Message { Text(String), Number(u32) } // Text("Hi"): 1 + (1 + 2) = 4 bytes // Number(42): 1 + 4 = 5 bytes ``` ## Next Steps - [Input/Output](https://polkadart.dev/scale-codec/input-output) - Master the I/O system - [Best Practices](https://polkadart.dev/scale-codec/best-practices) - Learn encoding best practices - [Examples](https://polkadart.dev/scale-codec/examples) - See real-world usage examples # Input and Output ## I/O System Overview The `polkadart_scale_codec` package uses an abstraction layer for input and output operations. This design allows codecs to work with different data sources and destinations without changing the encoding/decoding logic. ## Input Interface The `Input` mixin provides methods for reading encoded data. ### Creating Input There are multiple ways to create an `Input`: ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; // From hex string final input1 = Input.fromHex('0x2a000000'); // From bytes (List) final input2 = Input.fromBytes([42, 0, 0, 0]); // From Uint8List final bytes = Uint8List.fromList([42, 0, 0, 0]); final input3 = ByteInput(bytes); ``` ### Reading Data #### Single Byte ```dart final input = Input.fromHex('0x010203'); final byte1 = input.read(); // 1 final byte2 = input.read(); // 2 final byte3 = input.read(); // 3 ``` #### Multiple Bytes ```dart final input = Input.fromHex('0x01020304050607'); final bytes = input.readBytes(4); // [1, 2, 3, 4] // Remaining: [5, 6, 7] ``` #### Peeking (Non-Consuming) ```dart final input = Input.fromHex('0x010203'); // Peek without consuming final byte = input.peekByte(0); // 1 // Input position unchanged // Peek multiple bytes final bytes = input.peekBytes(0, 2); // [1, 2] // Input position still unchanged // Now consume final consumed = input.read(); // 1 // Now position advanced ``` ### Input State #### Remaining Length ```dart final input = Input.fromHex('0x0102030405'); print(input.remainingLength); // 5 input.readBytes(2); print(input.remainingLength); // 3 ``` #### Checking for Bytes ```dart final input = Input.fromHex('0x0102'); while (input.hasBytes()) { final byte = input.read(); print(byte); } print(input.hasBytes()); // false ``` #### Reset Offset ```dart final input = Input.fromHex('0x010203'); input.read(); // 1 input.read(); // 2 // Reset to beginning input.resetOffset(); input.read(); // 1 again ``` ### Cloning Input ```dart final input = Input.fromHex('0x010203'); // Clone for separate processing final clone = input.clone(); // Original and clone are independent input.read(); // 1 clone.read(); // 1 (separate position) ``` ### Hex Conversion ```dart final input = Input.fromBytes([1, 2, 3, 4]); // Get hex representation print(input.toHex()); // 0x01020304 ``` ### Accessing Buffer ```dart final input = Input.fromHex('0x01020304'); // Get underlying buffer final buffer = input.buffer; // Uint8List [1, 2, 3, 4] ``` ### End of Data Validation ```dart final input = Input.fromHex('0x2a'); final value = U8Codec.codec.decode(input); // Verify all data consumed input.assertEndOfDataReached(); // Throws if bytes remain // With custom message input.assertEndOfDataReached(' Expected complete message.'); ``` ## Output Interface The `Output` mixin provides methods for writing encoded data. ### Output Types #### ByteOutput Collects bytes into a buffer: ```dart final output = ByteOutput(); output.write([1, 2, 3]); output.pushByte(4); final bytes = output.toBytes(); // Uint8List [1, 2, 3, 4] ``` #### HexOutput Automatically converts to hex: ```dart final output = HexOutput(); output.write([42, 0, 0, 0]); print(output.toString()); // 0x2a000000 ``` #### GeneratorOutput For streaming output: ```dart final output = GeneratorOutput(); output.write([1, 2]); output.write([3, 4]); // Stream the data for (final byte in output.generator) { print(byte); } ``` #### SizeTracker Calculates size without storing data: ```dart final tracker = SizeTracker(); U32Codec.codec.encodeTo(42, tracker); StrCodec.codec.encodeTo('Hello', tracker); print(tracker.size); // Total bytes: 4 + 6 = 10 ``` ### Writing Data #### Single Byte ```dart final output = ByteOutput(); output.pushByte(42); output.pushByte(100); print(output.toBytes()); // [42, 100] ``` #### Multiple Bytes ```dart final output = ByteOutput(); output.write([1, 2, 3]); output.write([4, 5, 6]); print(output.toBytes()); // [1, 2, 3, 4, 5, 6] ``` ## Using with Codecs ### Encoding Pattern ```dart // Create output final output = ByteOutput(); // Encode using codec U32Codec.codec.encodeTo(42, output); BoolCodec.codec.encodeTo(true, output); StrCodec.codec.encodeTo('Hello', output); // Get result final encoded = output.toBytes(); // Or use helper final encoded2 = U32Codec.codec.encode(42); ``` ### Decoding Pattern ```dart // Create input from data final input = Input.fromBytes(encodedData); // Decode using codec final value1 = U32Codec.codec.decode(input); final value2 = BoolCodec.codec.decode(input); final value3 = StrCodec.codec.decode(input); // Verify all consumed input.assertEndOfDataReached(); ``` ## Complete Example: Custom Protocol Here's a complete example implementing a simple protocol: ```dart import 'package:polkadart_scale_codec/polkadart_scale_codec.dart'; class Message { final int version; final int messageType; final List payload; Message(this.version, this.messageType, this.payload); // Encode message Uint8List encode() { final output = ByteOutput(); // Header U8Codec.codec.encodeTo(version, output); U8Codec.codec.encodeTo(messageType, output); // Payload with length prefix CompactCodec.codec.encodeTo(payload.length, output); output.write(payload); return output.toBytes(); } // Decode message static Message decode(Uint8List data) { final input = ByteInput(data); // Read header final version = U8Codec.codec.decode(input); final messageType = U8Codec.codec.decode(input); // Read payload final payloadLength = CompactCodec.codec.decode(input); final payload = input.readBytes(payloadLength); // Verify complete input.assertEndOfDataReached(); return Message(version, messageType, payload); } // Get hex representation String toHex() { final output = HexOutput(); U8Codec.codec.encodeTo(version, output); U8Codec.codec.encodeTo(messageType, output); CompactCodec.codec.encodeTo(payload.length, output); output.write(payload); return output.toString(); } // Calculate encoded size int encodedSize() { final tracker = SizeTracker(); U8Codec.codec.encodeTo(version, tracker); U8Codec.codec.encodeTo(messageType, tracker); CompactCodec.codec.encodeTo(payload.length, tracker); tracker.write(payload); return tracker.size; } } void main() { // Create message final msg = Message(1, 42, [1, 2, 3, 4, 5]); print('Encoded size: ${msg.encodedSize()} bytes'); print('Hex: ${msg.toHex()}'); // Encode final encoded = msg.encode(); // Decode final decoded = Message.decode(encoded); print('Version: ${decoded.version}'); print('Type: ${decoded.messageType}'); print('Payload: ${decoded.payload}'); } ``` ## Advanced Patterns ### Streaming Decode For large data, process incrementally: ```dart void processLargeSequence(Input input) { // Read length final count = CompactCodec.codec.decode(input); // Process items one at a time for (var i = 0; i < count; i++) { final item = U32Codec.codec.decode(input); processItem(item); // Item processed and can be garbage collected } } void processItem(int item) { // Process individual item print('Processing: $item'); } ``` ### Conditional Decoding Read structure based on runtime data: ```dart void decodeConditional(Input input) { final version = U8Codec.codec.decode(input); if (version == 1) { // Version 1 format final data = U32Codec.codec.decode(input); } else if (version == 2) { // Version 2 format (different structure) final data1 = U64Codec.codec.decode(input); final data2 = BoolCodec.codec.decode(input); } } ``` ### Error Recovery Handle partial decoding: ```dart Result safeDecode(Uint8List data) { try { final input = ByteInput(data); final message = Message.decode(data); input.assertEndOfDataReached(); return Result.ok(message); } catch (e) { return Result.err('Decode failed: $e'); } } ``` ### Batched Encoding Encode multiple items efficiently: ```dart Uint8List encodeMany(List values) { final output = ByteOutput(); // Write count CompactCodec.codec.encodeTo(values.length, output); // Write all values for (final value in values) { U32Codec.codec.encodeTo(value, output); } return output.toBytes(); } ``` ## Performance Tips ### Pre-allocate Output ```dart // Calculate size first final expectedSize = codec.sizeHint(value); // Pre-allocate buffer final output = ByteOutput(expectedSize); // Encode (no reallocation needed) codec.encodeTo(value, output); ``` ### Reuse Outputs ```dart class Encoder { final ByteOutput _output = ByteOutput(); Uint8List encode(int value) { _output.clear(); // Clear previous data U32Codec.codec.encodeTo(value, _output); return _output.toBytes(); } } ``` ### Use Size Tracker ```dart // Don't encode just to get size final bytes = codec.encode(value); final size = bytes.length; // Wasteful // Use size tracker instead final size = codec.sizeHint(value); // Efficient ``` ## ByteInput Details The `ByteInput` class is the main implementation: ```dart class ByteInput with Input { final Uint8List _buffer; int offset = 0; ByteInput(this._buffer); @override int read() { return _buffer[offset++]; } @override Uint8List readBytes(int length) { final result = _buffer.sublist(offset, offset + length); offset += length; return Uint8List.fromList(result); } // ... other methods } ``` ### Buffer Management ```dart final buffer = Uint8List.fromList([1, 2, 3, 4]); final input = ByteInput(buffer); // Current position print(input.offset); // 0 // Read and advance input.read(); print(input.offset); // 1 // Reset input.resetOffset(); print(input.offset); // 0 ``` ## Common Patterns ### Encode-Decode Round-Trip ```dart void testRoundTrip(Codec codec, T value) { // Encode final encoded = codec.encode(value); // Decode final decoded = codec.decode(Input.fromBytes(encoded)); // Verify assert(decoded == value, 'Round-trip failed'); } ``` ### Partial Decode ```dart Map decodeHeader(Input input) { return { 'version': U8Codec.codec.decode(input), 'type': U8Codec.codec.decode(input), 'length': CompactCodec.codec.decode(input), }; // Input still has body data remaining } ``` ### Multi-Format Output ```dart void showMultiFormat(int value) { // Bytes final bytes = U32Codec.codec.encode(value); print('Bytes: $bytes'); // Hex final hexOutput = HexOutput(); U32Codec.codec.encodeTo(value, hexOutput); print('Hex: ${hexOutput.toString()}'); // Size final size = U32Codec.codec.sizeHint(value); print('Size: $size bytes'); } ``` ## Best Practices ::callout{type="tip"} ### Always Verify End of Data After decoding, verify all data was consumed: ```dart final decoded = codec.decode(input); input.assertEndOfDataReached(); ``` :: ::callout{type="tip"} ### Use Hex for Debugging HexOutput makes it easy to inspect encoded data: ```dart final output = HexOutput(); codec.encodeTo(value, output); print('Debug: ${output.toString()}'); ``` :: ::callout{type="caution"} ### Handle Buffer Exhaustion Always check remaining length: ```dart if (input.remainingLength! < 4) { throw Exception('Not enough data for u32'); } ``` :: ::callout{type="tip"} ### Clone for Lookahead When you need to inspect data without consuming: ```dart final clone = input.clone(); final peekedValue = codec.decode(clone); // Original input unchanged ``` :: ## Next Steps - [Best Practices](https://polkadart.dev/scale-codec/best-practices) - Learn encoding best practices - [Examples](https://polkadart.dev/scale-codec/examples) - See real-world usage examples - [substrate\_metadata](https://polkadart.dev/substrate-metadata) - Learn how SCALE integrates with metadata # Polkadart API We have two different ways to interact with the Polkadot or Polkadot based chains, through the Polkadart API and the network-specific APIs that are generated by the Polkadart CLI. In this section, we will focus on the Polkadart API. If you are interested in the network-specific APIs, you can find them in the [Blockchain API](https://polkadart.dev/metadata/overview) section. #### Installation The Polkadart API is available in the main package `polkadart` which provides tools to connect and interact with the Polkadot or Polkadot based chains. It abstracts the complexities of the network protocols and offers straightforward APIs. Add the following to your `pubspec.yaml` file: ```yaml [pubspec.yaml] dependencies: polkadart: ^1.0.0 ``` Get the dependencies by running: ```bash dart pub get ``` #### Sample usage ```dart [demo.dart] void main() async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final stateApi = StateApi(provider); final runtimeVersion = await stateApi.getRuntimeVersion(); print(runtimeVersion.toJson()); } ``` This will print the following output: ```dart { specName: polkadot, implName: parity-polkadot, authoringVersion: 0, specVersion: 1003003, implVersion: 0, apis: [...], transactionVersion: 26, stateVersion: 1 } ``` The Polkadart APIs are static and do not change between different networks. They provide the most common functionalities that are shared across all networks. # Author API The Author API provides a way to submit and watch extrinsics on the blockchain. This API is available through the `AuthorApi` class from the `polkadart` package. ### Sample usage ```dart void main() async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final author = AuthorApi(provider); final extrinsic = hex.decode('cd018400e6c8ca9e309a1cce16eee662a9b3886f0c7b7fddce5cd89db7a6eba0a2aa003b01b873dd1fe5a0b23641496be8d7f2938f06698ca07c1de6d75a4e6f18159aaa5fdca09d9bf50d71d80f4055e34c8a40097c3c9a34d8309dbe9ca97ff450b9a28700580000000024506f6c6b6164617274'); final submit = await author.submitAndWatchExtrinsic(extrinsic as Uint8List, (data) => print('From here: ${data.type} - ${data.value}')); } ``` If you send a valid extrinsic, you will get an output similar to this: ```bash Instance of '_ForwardingStreamSubscription, ExtrinsicStatus>' From here: broadcast - [12D3KooWEW8fqaHq47NfZx5M8VHpnGaPYFPKNyoRxHNxJNaftxex, 12D3KooWRXzBHd1cMG5DRGwo5Uw6AVB3NM35s9hJZJPLVXEjnJm1, 12D3KooWNhvdEMm58VzCogLN8zSY8M22z2BxW6HFSCzm7uxtWD4k, 12D3KooWFvW8WS6BgGqH9YwCzbYNDHt54eXu74MXsWTy5YBNVG8p, 12D3KooWKyCEReVMRfDJVMWPqYHkgNBxRuzBeGrh45gFNaRyo3xa, 12D3KooWR9ZQqixCCskgLs1iQMqFYYLsFdei1CQgg2TRkpuerU8r, 12D3KooWHVDNuABpm6oZ8ZCuiE4MvnMsZTxrURm92ESDexj1YCTw, 12D3KooWM1RwsLQPtGfDksczydDZvZjeY9ZcsanhzyJvPzCAHwpS, 12D3KooWGTYAoDHV656gGVydaG8HzPSLRKq7JYhkKCxLVEaWSZVH, 12D3KooWQXgbYeL1ueo2oD8aEktrKW57Nz2gmQocHuVknAevgWn8, 12D3KooWRCXsj3xpfQ1FAuPgTHetoF9CGEGifEPciY6vvgBZskm6, 12D3KooWRUuNjg4RE8zyXrrJWZsKCiPLMFa4KGVp88axt3SRRGs9, 12D3KooWE2ECqN3V6Y9CKfx4oth8EsF9MRcrkFsTFUv6SP8JMyLL, 12D3KooWEJr54XaHzGXwDNggSoAacmR8zWFymRAiYQa8cnRr9Syj, 12D3KooWNBfDPCootySrUTFVVFdbRzpGTEgfFM23PJVouY9Vd247, 12D3KooWHJBMZgt7ymAdTRtadPcGXpJw79vBGe8z53r9JMkZW7Ha, 12D3KooWBsWrDEmpvS3uV48LEv3Hn71UcjQHN5efBgncdrQwoCHK, 12D3KooWGP4bnFEY1mcR9XFjf7vTqM9Tr7ZejM3GmZF5UptCYBas, 12D3KooWPWRX8XUxV3pYeYb1fZpK2RSycTFhrVZ4Su65HxuYzRSh, 12D3KooWMcih1WAz1dhu1Wq86PgUamtHEkBgxRHiCFfPqqHk6Qfc, 12D3KooWNuVTLRUZ7L2Zr8eXkCRBdPxCYPukmkVFB5S5ivN7cQii, 12D3KooWNJQAKSRvvu35yUHTsJiaSDDQyn3smpme48ec5hEaw8pm, 12D3KooWEePxbZvZge79AZqhDeqgNLuxeXiQu8Dev7v5YRkr2wR2, 12D3KooWFX5YwGhDfyJ3d2XDNSMZ5CsM1tzGtRtZydCtVs2xybVz, 12D3KooWQVrexwN3D2QSV38FDw7cUpy9SR8Z1iEbTsNTKRT3cPy4, 12D3KooWRsDm136qDqn6KSYdWY1qe988DPNCVuCXs2Gr5s8Z8EdN, 12D3KooWBfLF5p1Ln6UXcbLebd5GsADYrzrgbZow9zUFrWc5ieQY, 12D3KooWDUjbGEP1fExf7LDze8iScY6ycsB8B7K3Gfnb2wwffhxK, 12D3KooWKeTsEemCLPktDzypvrdit1vp5Za4zjtJ47bphVzAbibP, 12D3KooWCsjxFCTatxzz1j1wUnhNfmW4wWGTJzJQF4bUTZFLybPH, 12D3KooWNk7QhDvH4vHcpWEtJzvzBhiUXjdLEEyTuhD9aYzmsxBj, 12D3KooWHkp51QVBwvdoSKt2VZwApGymVZe4NsfZ8yd6PVDHnfos, 12D3KooWLaCmjNjPtrEPz5EFtNNioNutYSPydbTQmMAgXvG6peKR, 12D3KooWBaPqD5By7WZF1yPiuyd8mmHDYv3FPx2wjrF2tYDA2vct, 12D3KooWQZkoqjez2aJ8GsTf1K5vCY2PV3XxwVf9B3iuuYYxViEE, 12D3KooWPhm3P3xV2FAa2pRwi751ofrM4dCX9E1cAkQXPgAXmUv2, 12D3KooWGAkVvjifuhNmNPRBzDqLeqQ9PzAMqZSC7jc7GXKxyKAM, 12D3KooWQvuM83h996TEAV91j7WXjeZXET3ZnnC1N6sLnAA7Ye7n, 12D3KooWDRJxWNZMd7LsyrwziDzjarHNYgRkCS9TZc7uGz1tS9KU, 12D3KooWBn8eMBgQz2WR6zNMoEfDvuQ3i9oMrXhtWK4c3UEHzAo6, 12D3KooWS7HX5pNMXCDPQP5dapVPPwgzPp6cPhVgsGFwkdpV4sEc, 12D3KooWLZxXQsbADZSQmUinWASqpZMNo9NdRr4DDcvFjtCwGfhi, 12D3KooWRehgtSgMoCBKN1Dd2G6btn13HMA6pgu4vU18GRVmyzic, 12D3KooWFhmerVkfFpS198agSBJuPvebhBAEn6zb569ogXvnzq8G, 12D3KooWQZF95Wre2JSUX5zMy5ACy6zkXSqjsUV615kfF7VY6d9N, 12D3KooWKZwp59WkysZYAKLUGLJhsYKHRhvNaXvhD7fBhhCuPDy1, 12D3KooWGk8zv6TJqYtJnJtSCyUTKH3xAQg8VvvuiGNRhQ2uK8AZ] From here: inBlock - 0x726aeb560cb25b7c731ddb7ec1df8f845ae31d2eeb98478e6f716a7e05a8ac5a From here: retracted - 0x726aeb560cb25b7c731ddb7ec1df8f845ae31d2eeb98478e6f716a7e05a8ac5a From here: inBlock - 0xaeaa329613992c9f06e4bff428acc49656209dad19b2829b020122058cf00aa5 From here: finalized - 0xaeaa329613992c9f06e4bff428acc49656209dad19b2829b020122058cf00aa5 ``` ## Methods There are just two methods available in the `AuthorApi` class, you can find them below: ### submitExtrinsic ```dart Future submitExtrinsic(Uint8List extrinsic) ``` Submit a fully formatted extrinsic for block inclusion. The type will be changed to Extrinsic later ### submitAndWatchExtrinsic ```dart Future> submitAndWatchExtrinsic( Uint8List extrinsic, Function(ExtrinsicStatus) onData) ``` Submits and subscribe to watch an extrinsic until unsubscribed. # Chain API The Chain API is mainly used to get information needed to build and sign transactions. This API is available through the `ChainApi` class from the `polkadart` package. ### Sample usage ```dart void main() async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final chainApi = ChainApi(provider); final header = await chainApi.getChainHeader(); print(header.toString()); } ``` You will get the following output: ```dart 23198099 ``` ## Methods There are a few methods available in the `ChainApi` class, you can find them below: ### getBlockHash ```dart Future getBlockHash({int? blockNumber}) ``` Returns the BlockHash for the given block number. If no block number is provided, it will return the hash of the latest block at chain height. ### getChainHeader ```dart Future getChainHeader({BlockHash? at}) ``` Get the latest block number or specific block number by BlockHash. # State API The State API is your gateway to querying blockchain state, including storage data, metadata, and runtime information. This API provides low-level access to the blockchain's state trie and is available through the `StateApi` class. ## Quick Example ```dart [Query Runtime Version] void main() async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final stateApi = StateApi(provider); // Get current runtime version final runtimeVersion = await stateApi.getRuntimeVersion(); print(runtimeVersion.toJson()); // Get metadata final metadata = await stateApi.getMetadata(); print('Metadata version: ${metadata.version}'); } ``` ### Expected Output ```dart { specName: polkadot, implName: parity-polkadot, authoringVersion: 0, specVersion: 1003003, implVersion: 0, apis: [...], transactionVersion: 26, stateVersion: 1 } ``` ## Core Methods ::callout{type="tip"} All methods are asynchronous and return `Future` types. Most methods accept an optional `BlockHash at` parameter to query historical state. :: The `StateApi` class provides comprehensive methods for querying blockchain state: ### Runtime Calls #### call ```dart Future call(String method, Uint8List bytes, {BlockHash? at}) ``` Execute a runtime API call at a specific block's state. **Use case:** Call runtime APIs for specialized queries not exposed through standard RPC. ### Storage Queries #### getPairs ```dart Future> getPairs(StorageKey prefix, {BlockHash? at}) ``` Retrieve all key-value pairs matching a prefix. **Example:** ```dart // Get all account balances final pairs = await stateApi.getPairs( hex.decode('26aa394eea5630e07c48ae0c9558cef7'), // System.Account prefix ); ``` #### getKeysPaged ```dart Future> getKeysPaged({ required StorageKey key, required int count, StorageKey? startKey, BlockHash? at }) ``` Query storage keys with pagination support. **Example:** ```dart // Get first 100 account keys final keys = await stateApi.getKeysPaged( key: accountPrefix, count: 100, ); // Get next 100 keys final nextKeys = await stateApi.getKeysPaged( key: accountPrefix, count: 100, startKey: keys.last, ); ``` #### getStorage ```dart Future getStorage(StorageKey key, {BlockHash? at}) ``` Retrieve raw storage data for a specific key. **Returns:** Raw SCALE-encoded data or `null` if the key doesn't exist. #### getStorageHash & getStorageSize ```dart Future getStorageHash(StorageKey key, {BlockHash? at}) Future getStorageSize(StorageKey key, {BlockHash? at}) ``` Get the hash or size of storage data without fetching the full value. **Use case:** Efficiently check if storage has changed or estimate data size. ### Historical Queries #### queryStorage ```dart Future> queryStorage( List keys, BlockHash fromBlock, {BlockHash? toBlock} ) ``` Query storage changes over a block range. ::callout{type="caution"} The first result contains the initial state. Subsequent results show only changes (diffs). :: **Example:** ```dart // Track balance changes over 100 blocks final changes = await stateApi.queryStorage( [balanceStorageKey], fromBlockHash, toBlock: toBlockHash, ); ``` #### queryStorageAt ```dart Future> queryStorageAt( List keys, {BlockHash? at} ) ``` Query multiple storage entries at a specific block. #### getReadProof ```dart Future getReadProof(List keys, {BlockHash? at}) ``` Generate a merkle proof for storage entries. **Use case:** Prove storage values to light clients or cross-chain bridges. ### Metadata & Runtime #### getMetadata ```dart Future getMetadata({BlockHash? at}) ``` Fetch the complete runtime metadata. **Contains:** - All pallets and their functions - Storage layouts - Types registry - Runtime APIs #### getTypedMetadata ```dart Future getTypedMetadata({BlockHash? at}) ``` Fetch typed metadata with full type information for code generation and advanced queries. **Use case:** Required for generating metadata hashes (RFC-78), type-safe storage decoding, and code generation tools. **Example:** ```dart final stateApi = StateApi(provider); final typedMetadata = await stateApi.getTypedMetadata(); // Access type registry print('Types count: ${typedMetadata.types.length}'); ``` #### getRuntimeVersion ```dart Future getRuntimeVersion({BlockHash? at}) ``` Get runtime version information. **Includes:** - `specVersion`: Runtime specification version - `specName`: Chain identifier (e.g., "polkadot") - `implVersion`: Implementation version - `apis`: Available runtime APIs ### Real-time Subscriptions #### subscribeRuntimeVersion ```dart Future> subscribeRuntimeVersion( Function(RuntimeVersion) onData ) ``` Subscribe to runtime version updates. **Example:** ```dart final subscription = await stateApi.subscribeRuntimeVersion((version) { print('Runtime upgraded to version ${version.specVersion}'); }); // Don't forget to cancel when done await subscription.cancel(); ``` #### subscribeStorage ```dart Future> subscribeStorage( List storageKeys, Function(StorageChangeSet) onData ) ``` Monitor storage changes in real-time. **Example:** ```dart // Watch balance changes for multiple accounts final subscription = await stateApi.subscribeStorage( [aliceBalanceKey, bobBalanceKey], (changes) { print('Storage updated at block ${changes.block}'); for (final change in changes.changes) { print('Key: ${hex.encode(change.$1)}'); print('New value: ${change.$2 != null ? hex.encode(change.$2!) : "deleted"}'); } }, ); ``` ## Best Practices ::tabs :::tab{label="Efficient Queries"} - Use `getKeysPaged` for large datasets to avoid timeouts - Query specific blocks for consistent reads across multiple calls - Use `getStorageHash` to detect changes before fetching full data ::::tab{label="Subscriptions"} * Always cancel subscriptions when done to prevent memory leaks * Handle reconnection logic for long-running subscriptions * Use error handlers for subscription failures :::::tab{label="Historical Data"} - Limit block ranges in `queryStorage` to avoid overwhelming responses - Cache metadata for specific block heights when querying historical data - Consider using archive nodes for deep historical queries ::::: :::: ::: :: ## Next Steps - Learn about [type-safe storage queries](https://polkadart.dev/metadata/overview) - Explore [transaction building](https://polkadart.dev/api/author) - Understand [chain synchronization](https://polkadart.dev/api/chain) # System API The System API provides a way to query the system information of the blockchain. It allows you to query the chain name, version, health, and more. This API is available through the `SystemApi` class from the `polkadart` package. ### Sample usage ```dart void main() async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final systemApi = SystemApi(provider); final chain = await systemApi.chain(); print(chain); } ``` You will get the following output: ```dart Polkadot ``` ## Methods There are several methods available in the `SystemApi` class that allow you to query the system information of the blockchain. Here are some of the most commonly used methods: ### name ```dart Future name() ``` Get the node's implementation name. Plain old string. ### version ```dart Future version() ``` Get the node implementation's version. Should be a semver string. ### chain ```dart Future chain() ``` Get the chain's name. Given as a string identifier. ### chainType ```dart Future chainType() ``` Get the chain's type. ### health ```dart Future health() ``` Return health status of the node. Node is considered healthy if it is: - connected to some peers (unless running in dev mode) - not performing a major sync ### localPeerId ```dart Future localPeerId() ``` Returns the base58-encoded PeerId of the node. ### localListenAddresses ```dart Future> localListenAddresses() ``` Returns the multi-addresses that the local node is listening on The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to be passed to `addReservedPeer` or as a bootnode address for example. ### peers ```dart Future>> peers() async { ``` Returns currently connected peers unsafe: This method is only active with appropriate flags ### accountNextIndex ```dart Future accountNextIndex(String account) ``` Returns the next valid index (aka nonce) for given account. This method takes into consideration all pending transactions currently in the pool and if no transactions are found in the pool it fallbacks to query the index from the runtime (aka. state nonce). ### dryRun ```dart Future dryRun(Uint8List extrinsic, {BlockHash? at}) ``` Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult. ### nodeRoles ```dart Future> nodeRoles() ``` Returns the roles the node is running as. ### properties ```dart Future> properties() async ``` Get a custom set of properties as a JSON object, defined in the chain spec. ### addReservedPeer ```dart Future addReservedPeer(String peer) ``` Adds a reserved peer. Returns the empty string or an error. The string parameter should encode a `p2p` multiaddr. `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV` is an example of a valid, passing multiaddr with PeerId attached. unsafe: This method is only active with appropriate flags ### removeReservedPeer ```dart Future removeReservedPeer(String peerId) ``` Remove a reserved peer. Returns the empty string or an error. The string should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`. unsafe: This method is only active with appropriate flags ### reservedPeers ```dart Future> reservedPeers() ``` Returns the list of reserved peers ### addLogFilter ```dart Future addLogFilter(String directives) ``` Adds the supplied directives to the current log filter unsafe: This method is only active with appropriate flags ### resetLogFilter ```dart Future resetLogFilter() ``` Resets the log filter to Substrate defaults unsafe: This method is only active with appropriate flags ### syncState ```dart Future syncState() ``` Returns the state of the syncing of the node: starting block, current best block, highest known block. # Overview In this section we will show you how to interact with network-specific APIs that are generated by the Polkadart CLI. These APIs are specific to the network you are connecting to and provide all the methods available on that network in a type-safe way. #### Installation To have access to the network-specific APIs, you need to install the Polkadart CLI and generate the types for the network you are interested in. If you already did that on the [Getting Started](https://polkadart.dev/getting-started/installation) section, you can skip this step. Add the following to your `pubspec.yaml` file: ```yaml [pubspec.yaml] dependencies: polkadart_cli: ^1.0.0 ``` Get the dependencies by running: ```bash dart pub get ``` #### Generating the APIs Now let's configure Polkadart CLI to generate the types for us, add the following configuration at the end of your pubspec file. ```yaml [pubspec.yaml] polkadart: output_dir: lib/generated chains: polkadot: wss://rpc.polkadot.io kusama: wss://kusama-rpc.polkadot.io ``` And finally let's generate the types: ```bash dart run polkadart_cli:generate -v ``` You will see that a folder `lib/generated` was created for you and it contains all APIs, types and extrinsics for the networks you specified. We will provide the same example as the one in the [Polkadart API](https://polkadart.dev/api/overview) section, but this time using the network-specific APIs. ## Example: StateApi ```dart [demo.dart] Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final runtime = await polkadot.rpc.state.getRuntimeVersion(); print(runtime.toJson()); } ``` You will get the same output as the Polkadart API example on [StateApi](https://polkadart.dev/api/state#sample-usage) section: ```dart { specName: polkadot, implName: parity-polkadot, authoringVersion: 0, specVersion: 1003003, implVersion: 0, apis: [...], transactionVersion: 26, stateVersion: 1 } ``` ## Example: SystemApi ```dart [demo.dart] void main() async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final chain = await polkadot.rpc.system.chain(); print(chain); } ``` You will get the same output as the Polkadart API example on [SystemApi](https://polkadart.dev/api/system#sample-usage) section: ```dart Polkadot ``` ## Next steps The following sections will provide more details on the available APIs for polkadot network. The APIs are generated based on a snapshot of the network in the time of writing, please keep in mind that the network may have changed since then. # Constants ## Reference ### system #### blockWeights ```dart final _i20.BlockWeights blockWeights = _i20.BlockWeights( baseBlock: _i15.Weight( refTime: BigInt.from(13806190000), proofSize: BigInt.zero, ), maxBlock: _i15.Weight( refTime: BigInt.from(2000000000000), proofSize: BigInt.parse( '18446744073709551615', radix: 10, ), ), perClass: _i21.PerDispatchClass( normal: _i22.WeightsPerClass( baseExtrinsic: _i15.Weight( refTime: BigInt.from(126045000), proofSize: BigInt.zero, ), maxExtrinsic: _i15.Weight( refTime: BigInt.from(1479873955000), proofSize: BigInt.parse( '13650590614545068195', radix: 10, ), ), maxTotal: _i15.Weight( refTime: BigInt.from(1500000000000), proofSize: BigInt.parse( '13835058055282163711', radix: 10, ), ), reserved: _i15.Weight( refTime: BigInt.zero, proofSize: BigInt.zero, ), ), operational: _i22.WeightsPerClass( baseExtrinsic: _i15.Weight( refTime: BigInt.from(126045000), proofSize: BigInt.zero, ), maxExtrinsic: _i15.Weight( refTime: BigInt.from(1979873955000), proofSize: BigInt.parse( '18262276632972456099', radix: 10, ), ), maxTotal: _i15.Weight( refTime: BigInt.from(2000000000000), proofSize: BigInt.parse( '18446744073709551615', radix: 10, ), ), reserved: _i15.Weight( refTime: BigInt.from(500000000000), proofSize: BigInt.parse( '4611686018427387904', radix: 10, ), ), ), mandatory: _i22.WeightsPerClass( baseExtrinsic: _i15.Weight( refTime: BigInt.from(126045000), proofSize: BigInt.zero, ), maxExtrinsic: null, maxTotal: null, reserved: null, ), ), ); ``` Block & extrinsics weights: base values and limits. #### blockLength ```dart final _i23.BlockLength blockLength = const _i23.BlockLength( max: _i24.PerDispatchClass( normal: 3932160, operational: 5242880, mandatory: 5242880, )); ``` The maximum length of a block (in bytes). #### blockHashCount ```dart final int blockHashCount = 4096; ``` Maximum number of block number to block hash mappings to keep (oldest pruned first). #### dbWeight ```dart final _i25.RuntimeDbWeight dbWeight = _i25.RuntimeDbWeight( read: BigInt.from(20499000), write: BigInt.from(83471000), ); ``` The weight of runtime database operations the runtime can invoke. #### version ```dart final _i26.RuntimeVersion version = const _i26.RuntimeVersion( specName: 'polkadot', implName: 'parity-polkadot', authoringVersion: 0, specVersion: 1003000, implVersion: 0, apis: [ _i19.Tuple2, int>( [ 223, 106, 203, 104, 153, 7, 96, 155, ], 5, ), _i19.Tuple2, int>( [ 55, 227, 151, 252, 124, 145, 245, 228, ], 2, ), _i19.Tuple2, int>( [ 64, 254, 58, 212, 1, 248, 149, 154, ], 6, ), _i19.Tuple2, int>( [ 23, 166, 188, 13, 0, 98, 174, 179, ], 1, ), _i19.Tuple2, int>( [ 24, 239, 88, 163, 182, 123, 167, 112, ], 1, ), _i19.Tuple2, int>( [ 210, 188, 152, 151, 238, 208, 143, 21, ], 3, ), _i19.Tuple2, int>( [ 247, 139, 39, 139, 229, 63, 69, 76, ], 2, ), _i19.Tuple2, int>( [ 175, 44, 2, 151, 162, 62, 109, 61, ], 11, ), _i19.Tuple2, int>( [ 73, 234, 175, 27, 84, 138, 12, 176, ], 3, ), _i19.Tuple2, int>( [ 145, 213, 223, 24, 176, 210, 207, 88, ], 2, ), _i19.Tuple2, int>( [ 42, 94, 146, 70, 85, 57, 158, 96, ], 1, ), _i19.Tuple2, int>( [ 237, 153, 197, 172, 178, 94, 237, 245, ], 3, ), _i19.Tuple2, int>( [ 203, 202, 37, 227, 159, 20, 35, 135, ], 2, ), _i19.Tuple2, int>( [ 104, 122, 212, 74, 211, 127, 3, 194, ], 1, ), _i19.Tuple2, int>( [ 171, 60, 5, 114, 41, 31, 235, 139, ], 1, ), _i19.Tuple2, int>( [ 188, 157, 137, 144, 79, 91, 146, 63, ], 1, ), _i19.Tuple2, int>( [ 55, 200, 187, 19, 80, 169, 162, 168, ], 4, ), _i19.Tuple2, int>( [ 243, 255, 20, 213, 171, 82, 112, 89, ], 3, ), _i19.Tuple2, int>( [ 111, 245, 46, 232, 88, 230, 197, 189, ], 1, ), _i19.Tuple2, int>( [ 145, 177, 200, 177, 99, 40, 235, 146, ], 1, ), _i19.Tuple2, int>( [ 159, 251, 80, 90, 167, 56, 214, 156, ], 1, ), _i19.Tuple2, int>( [ 251, 197, 119, 185, 215, 71, 239, 214, ], 1, ), ], transactionVersion: 26, stateVersion: 1, ); ``` Get the chain's in-code version. #### S58Prefix ```dart final int sS58Prefix = 0; ``` The designated SS58 prefix of this chain. This replaces the "ss58Format" property declared in the chain spec. Reason is that the runtime should know about the prefix in order to make use of it as an identifier of the chain. ### scheduler #### maximumWeight ```dart final _i10.Weight maximumWeight = _i10.Weight( refTime: BigInt.from(1600000000000), proofSize: BigInt.parse( '14757395258967641292', radix: 10, ), ); ``` The maximum weight that may be scheduled per block for any dispatchables. #### maxScheduledPerBlock ```dart final int maxScheduledPerBlock = 50; ``` The maximum number of scheduled calls in the queue for a single block. NOTE: Dependent pallets' benchmarks might require a higher limit for the setting. Set a higher limit under `runtime-benchmarks` feature. ### babe #### epochDuration ```dart final BigInt epochDuration = BigInt.from(2400); ``` The amount of time, in slots, that each epoch should last. NOTE: Currently it is not possible to change the epoch duration after the chain has started. Attempting to do so will brick block production. #### expectedBlockTime ```dart final BigInt expectedBlockTime = BigInt.from(6000); ``` The expected average block time at which BABE should be creating blocks. Since BABE is probabilistic it is not trivial to figure out what the expected average block time should be based on the slot duration and the security parameter `c` (where `1 - c` represents the probability of a slot being empty). #### maxAuthorities ```dart final int maxAuthorities = 100000; ``` Max number of authorities allowed #### maxNominators ```dart final int maxNominators = 512; ``` The maximum number of nominators for each validator. ### timestamp #### minimumPeriod ```dart final BigInt minimumPeriod = BigInt.from(3000); ``` The minimum period between blocks. Be aware that this is different to the *expected* period that the block production apparatus provides. Your chosen consensus system will generally work with this to determine a sensible block time. For example, in the Aura pallet it will be double this period on default settings. ### indices #### deposit ```dart final BigInt deposit = BigInt.from(100000000000); ``` The deposit needed for reserving an index. ### balances #### existentialDeposit ```dart final BigInt existentialDeposit = BigInt.from(10000000000); ``` The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO! If you *really* need it to be zero, you can enable the feature `insecure_zero_ed` for this pallet. However, you do so at your own risk: this will open up a major DoS vector. In case you have multiple sources of provider references, you may also get unexpected behaviour if you set this to zero. Bottom line: Do yourself a favour and make it at least one! #### maxLocks ```dart final int maxLocks = 50; ``` The maximum number of locks that should exist on an account. Not strictly enforced, but used for weight estimation. Use of locks is deprecated in favour of freezes. See `https://github.com/paritytech/substrate/pull/12951/` #### maxReserves ```dart final int maxReserves = 50; ``` The maximum number of named reserves that can exist on an account. Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/` #### maxFreezes ```dart final int maxFreezes = 8; ``` The maximum number of individual freeze locks that can exist on an account at any time. ### transactionPayment #### operationalFeeMultiplier ```dart final int operationalFeeMultiplier = 5; ``` A fee multiplier for `Operational` extrinsics to compute "virtual tip" to boost their `priority` This value is multiplied by the `final_fee` to obtain a "virtual tip" that is later added to a tip component in regular `priority` calculations. It means that a `Normal` transaction can front-run a similarly-sized `Operational` extrinsic (with no tip), by including a tip value greater than the virtual tip. ```rust For `Normal` let priority = priority_calc(tip); For `Operational` let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier; let priority = priority_calc(tip + virtual_tip); ``` Note that since we use `final_fee` the multiplier applies also to the regular `tip` sent with the transaction. So, not only does the transaction get a priority bump based on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational` transactions. ### staking #### historyDepth ```dart final int historyDepth = 84; ``` Number of eras to keep in history. Following information is kept for eras in `[current_era - HistoryDepth, current_era]`: `ErasStakers`, `ErasStakersClipped`, `ErasValidatorPrefs`, `ErasValidatorReward`, `ErasRewardPoints`, `ErasTotalStake`, `ErasStartSessionIndex`, `ClaimedRewards`, `ErasStakersPaged`, `ErasStakersOverview`. Must be more than the number of eras delayed by session. I.e. active era must always be in history. I.e. `active_era > current_era - history_depth` must be guaranteed. If migrating an existing pallet from storage value to config value, this should be set to same value or greater as in storage. Note: `HistoryDepth` is used as the upper bound for the `BoundedVec` item `StakingLedger.legacy_claimed_rewards`. Setting this value lower than the existing value can lead to inconsistencies in the `StakingLedger` and will need to be handled properly in a migration. The test `reducing_history_depth_abrupt` shows this effect. #### sessionsPerEra ```dart final int sessionsPerEra = 6; ``` Number of sessions per era. #### bondingDuration ```dart final int bondingDuration = 28; ``` Number of eras that staked funds must remain bonded for. #### slashDeferDuration ```dart final int slashDeferDuration = 27; ``` Number of eras that slashes are deferred by, after computation. This should be less than the bonding duration. Set to 0 if slashes should be applied immediately, without opportunity for intervention. #### maxExposurePageSize ```dart final int maxExposurePageSize = 512; ``` The maximum size of each `T::ExposurePage`. An `ExposurePage` is weakly bounded to a maximum of `MaxExposurePageSize` nominators. For older non-paged exposure, a reward payout was restricted to the top `MaxExposurePageSize` nominators. This is to limit the i/o cost for the nominator payout. Note: `MaxExposurePageSize` is used to bound `ClaimedRewards` and is unsafe to reduce without handling it in a migration. #### maxUnlockingChunks ```dart final int maxUnlockingChunks = 32; ``` The maximum number of `unlocking` chunks a `StakingLedger` can have. Effectively determines how many unique eras a staker may be unbonding in. Note: `MaxUnlockingChunks` is used as the upper bound for the `BoundedVec` item `StakingLedger.unlocking`. Setting this value lower than the existing value can lead to inconsistencies in the `StakingLedger` and will need to be handled properly in a runtime migration. The test `reducing_max_unlocking_chunks_abrupt` shows this effect. ### grandpa #### maxAuthorities ```dart final int maxAuthorities = 100000; ``` Max Authorities in use #### maxNominators ```dart final int maxNominators = 512; ``` The maximum number of nominators for each validator. #### maxSetIdSessionEntries ```dart final BigInt maxSetIdSessionEntries = BigInt.from(168); ``` The maximum number of entries to keep in the set id to session index mapping. Since the `SetIdSession` map is only used for validating equivocations this value should relate to the bonding duration of whatever staking system is being used (if any). If equivocation handling is not enabled then this value can be zero. ### treasury #### spendPeriod ```dart final int spendPeriod = 345600; ``` Period between successive spends. #### burn ```dart final _i12.Permill burn = 10000; ``` Percentage of spare funds (if any) that are burnt per spend period. #### palletId ```dart final _i13.PalletId palletId = const [ 112, 121, 47, 116, 114, 115, 114, 121, ]; ``` The treasury's pallet id, used for deriving its sovereign account ID. #### maxApprovals ```dart final int maxApprovals = 100; ``` The maximum number of approvals that can wait in the spending queue. NOTE: This parameter is also used within the Bounties Pallet extension if enabled. #### payoutPeriod ```dart final int payoutPeriod = 432000; ``` The period during which an approved treasury spend has to be claimed. ### convictionVoting #### maxVotes ```dart final int maxVotes = 512; ``` The maximum number of concurrent votes an account may have. Also used to compute weight, an overly large value can lead to extrinsics with large weight estimation: see `delegate` for instance. #### voteLockingPeriod ```dart final int voteLockingPeriod = 100800;\ ``` The minimum period of vote locking. It should be no shorter than enactment period to ensure that in the case of an approval, those successful voters are locked into the consequences that their votes entail. ### referenda #### submissionDeposit ```dart final BigInt submissionDeposit = BigInt.from(10000000000); ``` The minimum amount to be used as a deposit for a public referendum proposal. #### maxQueued ```dart final int maxQueued = 100; ``` Maximum size of the referendum queue for a single track. #### undecidingTimeout ```dart final int undecidingTimeout = 201600; ``` The number of blocks after submission that a referendum must begin being decided by. Once this passes, then anyone may cancel the referendum. #### alarmInterval ```dart final int alarmInterval = 1; ``` Quantization level for the referendum wakeup scheduler. A higher number will result in fewer storage reads/writes needed for smaller voters, but also result in delays to the automatic referendum status changes. Explicit servicing instructions are unaffected. #### tracks ```dart final List<_i4.Tuple2> tracks = [ _i4.Tuple2( 0, _i13.TrackInfo( name: 'root', maxDeciding: 1, decisionDeposit: BigInt.from(1000000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 14400, minEnactmentPeriod: 14400, minApproval: _i14.Reciprocal( factor: BigInt.from(222222224), xOffset: BigInt.from(333333335), yOffset: BigInt.from(333333332), ), minSupport: const _i14.LinearDecreasing( length: 1000000000, floor: 0, ceil: 500000000, ), ), ), _i4.Tuple2( 1, _i13.TrackInfo( name: 'whitelisted_caller', maxDeciding: 100, decisionDeposit: BigInt.from(100000000000000), preparePeriod: 300, decisionPeriod: 403200, confirmPeriod: 100, minEnactmentPeriod: 100, minApproval: _i14.Reciprocal( factor: BigInt.from(270899180), xOffset: BigInt.from(389830523), yOffset: BigInt.from(305084738), ), minSupport: _i14.Reciprocal( factor: BigInt.from(8650766), xOffset: BigInt.from(18867926), yOffset: BigInt.from(41509433), ), ), ), _i4.Tuple2( 2, _i13.TrackInfo( name: 'wish_for_change', maxDeciding: 10, decisionDeposit: BigInt.from(200000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 14400, minEnactmentPeriod: 100, minApproval: _i14.Reciprocal( factor: BigInt.from(222222224), xOffset: BigInt.from(333333335), yOffset: BigInt.from(333333332), ), minSupport: const _i14.LinearDecreasing( length: 1000000000, floor: 0, ceil: 500000000, ), ), ), _i4.Tuple2( 10, _i13.TrackInfo( name: 'staking_admin', maxDeciding: 10, decisionDeposit: BigInt.from(50000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 1800, minEnactmentPeriod: 100, minApproval: const _i14.LinearDecreasing( length: 607142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(7892829), xOffset: BigInt.from(15544040), yOffset: BigInt.from(-7772020), ), ), ), _i4.Tuple2( 11, _i13.TrackInfo( name: 'treasurer', maxDeciding: 10, decisionDeposit: BigInt.from(10000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 100800, minEnactmentPeriod: 14400, minApproval: _i14.Reciprocal( factor: BigInt.from(222222224), xOffset: BigInt.from(333333335), yOffset: BigInt.from(333333332), ), minSupport: const _i14.LinearDecreasing( length: 1000000000, floor: 0, ceil: 500000000, ), ), ), _i4.Tuple2( 12, _i13.TrackInfo( name: 'lease_admin', maxDeciding: 10, decisionDeposit: BigInt.from(50000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 1800, minEnactmentPeriod: 100, minApproval: const _i14.LinearDecreasing( length: 607142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(7892829), xOffset: BigInt.from(15544040), yOffset: BigInt.from(-7772020), ), ), ), _i4.Tuple2( 13, _i13.TrackInfo( name: 'fellowship_admin', maxDeciding: 10, decisionDeposit: BigInt.from(50000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 1800, minEnactmentPeriod: 100, minApproval: const _i14.LinearDecreasing( length: 607142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(7892829), xOffset: BigInt.from(15544040), yOffset: BigInt.from(-7772020), ), ), ), _i4.Tuple2( 14, _i13.TrackInfo( name: 'general_admin', maxDeciding: 10, decisionDeposit: BigInt.from(50000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 1800, minEnactmentPeriod: 100, minApproval: _i14.Reciprocal( factor: BigInt.from(222222224), xOffset: BigInt.from(333333335), yOffset: BigInt.from(333333332), ), minSupport: _i14.Reciprocal( factor: BigInt.from(49586777), xOffset: BigInt.from(90909091), yOffset: BigInt.from(-45454546), ), ), ), _i4.Tuple2( 15, _i13.TrackInfo( name: 'auction_admin', maxDeciding: 10, decisionDeposit: BigInt.from(50000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 1800, minEnactmentPeriod: 100, minApproval: _i14.Reciprocal( factor: BigInt.from(222222224), xOffset: BigInt.from(333333335), yOffset: BigInt.from(333333332), ), minSupport: _i14.Reciprocal( factor: BigInt.from(49586777), xOffset: BigInt.from(90909091), yOffset: BigInt.from(-45454546), ), ), ), _i4.Tuple2( 20, _i13.TrackInfo( name: 'referendum_canceller', maxDeciding: 1000, decisionDeposit: BigInt.from(100000000000000), preparePeriod: 1200, decisionPeriod: 100800, confirmPeriod: 1800, minEnactmentPeriod: 100, minApproval: const _i14.LinearDecreasing( length: 607142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(7892829), xOffset: BigInt.from(15544040), yOffset: BigInt.from(-7772020), ), ), ), _i4.Tuple2( 21, _i13.TrackInfo( name: 'referendum_killer', maxDeciding: 1000, decisionDeposit: BigInt.from(500000000000000), preparePeriod: 1200, decisionPeriod: 403200, confirmPeriod: 1800, minEnactmentPeriod: 100, minApproval: const _i14.LinearDecreasing( length: 607142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(7892829), xOffset: BigInt.from(15544040), yOffset: BigInt.from(-7772020), ), ), ), _i4.Tuple2( 30, _i13.TrackInfo( name: 'small_tipper', maxDeciding: 200, decisionDeposit: BigInt.from(10000000000), preparePeriod: 10, decisionPeriod: 100800, confirmPeriod: 100, minEnactmentPeriod: 10, minApproval: const _i14.LinearDecreasing( length: 357142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(1620729), xOffset: BigInt.from(3231018), yOffset: BigInt.from(-1615509), ), ), ), _i4.Tuple2( 31, _i13.TrackInfo( name: 'big_tipper', maxDeciding: 100, decisionDeposit: BigInt.from(100000000000), preparePeriod: 100, decisionPeriod: 100800, confirmPeriod: 600, minEnactmentPeriod: 100, minApproval: const _i14.LinearDecreasing( length: 357142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(4149097), xOffset: BigInt.from(8230453), yOffset: BigInt.from(-4115227), ), ), ), _i4.Tuple2( 32, _i13.TrackInfo( name: 'small_spender', maxDeciding: 50, decisionDeposit: BigInt.from(1000000000000), preparePeriod: 2400, decisionPeriod: 403200, confirmPeriod: 28800, minEnactmentPeriod: 14400, minApproval: const _i14.LinearDecreasing( length: 607142857, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(7892829), xOffset: BigInt.from(15544040), yOffset: BigInt.from(-7772020), ), ), ), _i4.Tuple2( 33, _i13.TrackInfo( name: 'medium_spender', maxDeciding: 50, decisionDeposit: BigInt.from(2000000000000), preparePeriod: 2400, decisionPeriod: 403200, confirmPeriod: 57600, minEnactmentPeriod: 14400, minApproval: const _i14.LinearDecreasing( length: 821428571, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(14377233), xOffset: BigInt.from(27972031), yOffset: BigInt.from(-13986016), ), ), ), _i4.Tuple2( 34, _i13.TrackInfo( name: 'big_spender', maxDeciding: 50, decisionDeposit: BigInt.from(4000000000000), preparePeriod: 2400, decisionPeriod: 403200, confirmPeriod: 100800, minEnactmentPeriod: 14400, minApproval: const _i14.LinearDecreasing( length: 1000000000, floor: 500000000, ceil: 1000000000, ), minSupport: _i14.Reciprocal( factor: BigInt.from(28326977), xOffset: BigInt.from(53763445), yOffset: BigInt.from(-26881723), ), ), ), ``` Information concerning the different referendum tracks. ### claims #### prefix ```dart final List prefix = const [ 80, 97, 121, 32, 68, 79, 84, 115, 32, 116, 111, 32, 116, 104, 101, 32, 80, 111, 108, 107, 97, 100, 111, 116, 32, 97, 99, 99, 111, 117, 110, 116, 58, ]; ``` ### vesting #### minVestedTransfer ```dart final BigInt minVestedTransfer = BigInt.from(10000000000); ``` The minimum amount transferred to call `vested_transfer`. #### maxVestingSchedules ```dart final int maxVestingSchedules = 28; ``` ### utility #### batchedCallsLimit ```dart final int batchedCallsLimit = 10922; ``` The limit on the number of batched calls. ### proxy #### proxyDepositBase ```dart final BigInt proxyDepositBase = BigInt.from(200080000000); ``` The base amount of currency needed to reserve for creating a proxy. This is held for an additional storage item whose value size is `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes. #### proxyDepositFactor ```dart final BigInt proxyDepositFactor = BigInt.from(330000000); ``` The amount of currency needed per proxy added. This is held for adding 32 bytes plus an instance of `ProxyType` more into a pre-existing storage value. Thus, when configuring `ProxyDepositFactor` one should take into account `32 + proxy_type.encode().len()` bytes of data. #### maxProxies ```dart final int maxProxies = 32; ``` The maximum amount of proxies allowed for a single account. #### maxPending ```dart final int maxPending = 32; ``` The maximum amount of time-delayed announcements that are allowed to be pending. #### announcementDepositBase ```dart final BigInt announcementDepositBase = BigInt.from(200080000000); ``` The base amount of currency needed to reserve for creating an announcement. This is held when a new storage item holding a `Balance` is created (typically 16 bytes). #### announcementDepositFactor ```dart final BigInt announcementDepositFactor = BigInt.from(660000000); ``` The amount of currency needed per announcement made. This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes) into a pre-existing storage value. ### multisig #### depositBase ```dart final BigInt depositBase = BigInt.from(200880000000); ``` The base amount of currency needed to reserve for creating a multisig execution or to store a dispatch call for later. This is held for an additional storage item whose value size is `4 + sizeof((BlockNumber, Balance, AccountId))` bytes and whose key size is `32 + sizeof(AccountId)` bytes. #### depositFactor ```dart final BigInt depositFactor = BigInt.from(320000000); ``` The amount of currency needed per unit threshold when creating a multisig execution. This is held for adding 32 bytes more into a pre-existing storage value. #### maxSignatories ```dart final int maxSignatories = 100; ``` The maximum amount of signatories allowed in the multisig. ### bounties #### bountyDepositBase ```dart final BigInt bountyDepositBase = BigInt.from(10000000000); ``` The amount held on deposit for placing a bounty proposal. #### bountyDepositPayoutDelay ```dart final int bountyDepositPayoutDelay = 0; ``` The delay period for which a bounty beneficiary need to wait before claim the payout. #### bountyUpdatePeriod ```dart final int bountyUpdatePeriod = 1296000; ``` Bounty duration in blocks. #### curatorDepositMultiplier ```dart final _i9.Permill curatorDepositMultiplier = 500000; ``` The curator deposit is calculated as a percentage of the curator fee. This deposit has optional upper and lower bounds with `CuratorDepositMax` and `CuratorDepositMin`. #### curatorDepositMax ```dart final BigInt? curatorDepositMax = BigInt.from(2000000000000); ``` Maximum amount of funds that should be placed in a deposit for making a proposal. #### curatorDepositMin ```dart final BigInt? curatorDepositMin = BigInt.from(100000000000); ``` Minimum amount of funds that should be placed in a deposit for making a proposal. #### bountyValueMinimum ```dart final BigInt bountyValueMinimum = BigInt.from(100000000000); ``` Minimum value for a bounty. #### dataDepositPerByte ```dart final BigInt dataDepositPerByte = BigInt.from(100000000); ``` The amount held on deposit per byte within the tip report reason or bounty description. #### maximumReasonLength ```dart final int maximumReasonLength = 16384; ``` Maximum acceptable reason length. Benchmarks depend on this value, be sure to update weights file when changing this value ### childBounties #### maxActiveChildBountyCount ```dart Maximum number of child bounties that can be added to a parent bounty. ``` Maximum number of child bounties that can be added to a parent bounty. #### childBountyValueMinimum ```dart final BigInt childBountyValueMinimum = BigInt.from(10000000000); ``` Minimum value for a child-bounty. ### electionProviderMultiPhase #### betterSignedThreshold ```dart final _i17.Perbill betterSignedThreshold = 0; ``` The minimum amount of improvement to the solution score that defines a solution as "better" in the Signed phase. #### offchainRepeat ```dart final int offchainRepeat = 18; ``` The repeat threshold of the offchain worker. For example, if it is 5, that means that at least 5 blocks will elapse between attempts to submit the worker's solution. #### minerTxPriority ```dart final BigInt minerTxPriority = BigInt.parse( '16602069666338596453', radix: 10, ); ``` The priority of the unsigned transaction submitted in the unsigned-phase #### signedMaxSubmissions ```dart final int signedMaxSubmissions = 16; ``` Maximum number of signed submissions that can be queued. It is best to avoid adjusting this during an election, as it impacts downstream data structures. In particular, `SignedSubmissionIndices` is bounded on this value. If you update this value during an election, you *must* ensure that `SignedSubmissionIndices.len()` is less than or equal to the new value. Otherwise, attempts to submit new solutions may cause a runtime panic. #### signedMaxWeight ```dart final _i18.Weight signedMaxWeight = _i18.Weight( refTime: BigInt.from(1466067765000), proofSize: BigInt.parse( '13650590614545068195', radix: 10, ), ); ``` Maximum weight of a signed solution. If `Config::MinerConfig` is being implemented to submit signed solutions (outside of this pallet), then `MinerConfig::solution_weight` is used to compare against this value. #### signedMaxRefunds ```dart final int signedMaxRefunds = 4; ``` The maximum amount of unchecked solutions to refund the call fee for. #### signedRewardBase ```dart final BigInt signedRewardBase = BigInt.from(10000000000); ``` Base reward for a signed solution #### signedDepositByte ```dart final BigInt signedDepositByte = BigInt.from(97656); ``` Per-byte deposit for a signed solution. #### signedDepositWeight ```dart Per-weight deposit for a signed solution. ``` final BigInt signedDepositWeight = BigInt.zero; #### maxWinners ```dart final int maxWinners = 1200; ``` The maximum number of winners that can be elected by this `ElectionProvider` implementation. Note: This must always be greater or equal to `T::DataProvider::desired_targets()`. #### minerMaxLength ```dart final int minerMaxLength = 3538944; ``` #### minerMaxWeight ```dart final _i18.Weight minerMaxWeight = _i18.Weight( refTime: BigInt.from(1466067765000), proofSize: BigInt.parse( '13650590614545068195', radix: 10, ), ); ``` #### minerMaxVotesPerVoter ```dart final int minerMaxVotesPerVoter = 16; ``` #### minerMaxWinners ```dart final int minerMaxWinners = 1200; ``` ### voterList #### bagThresholds ```dart final List bagThresholds = const [ 10000000000, 11131723507, 12391526824, 13793905044, 15354993703, 17092754435, 19027181634, 21180532507, 23577583160, 26245913670, 29216225417, 32522694326, 36203364094, 40300583912, 44861495728, 49938576656, 55590242767, 61881521217, 68884798439, 76680653006, 85358782760, 95019036859, 105772564622, 117743094401, 131068357174, 145901671259, 162413706368, 180794447305, 201255379901, 224031924337, 249386143848, 277609759981, 309027509097, 344000878735, 382932266827, 426269611626, 474511545609, 528213132664, 587992254562, 654536720209, 728612179460, 811070932564, 902861736593, 1005040721687, 1118783542717, 1245398906179, 1386343627960, 1543239395225, 1717891425287, 1912309236147, 2128729767682, 2369643119512, 2637821201686, 2936349627828, 3268663217709, 3638585517729, 4050372794022, 4508763004364, 5019030312352, 5587045771074, 6219344874498, 6923202753807, 7706717883882, 8578905263043, 9549800138161, 10630573468586, 11833660457397, 13172903628838, 14663712098160, 16323238866411, 18170578180087, 20226985226447, 22516120692255, 25064322999817, 27900911352605, 31058523077268, 34573489143434, 38486252181966, 42841831811331, 47690342626046, 53087570807094, 59095615988698, 65783605766662, 73228491069308, 81515931542404, 90741281135191, 101010685227495, 112442301921293, 125167661548718, 139333180038781, 155101843555358, 172655083789626, 192194865483744, 213946010204502, 238158783103893, 265111772429462, 295115094915607, 328513963936552, 365692661475578, 407078959611349, 453149042394237, 504432984742966, 561520851400862, 625069486125324, 695810069225823, 774556530406243, 862214913708369, 959793802308039, 1068415923109985, 1189331064661951, 1323930457019515, 1473762779014021, 1640551977100649, 1826217100807404, 2032894383008501, 2262961819074188, 2519066527700738, 2804155208229882, 3121508044894685, 3474776448088622, 3868025066902796, 4305778556320752, 4793073637166665, 5335517047800242, 5939350054341159, 6611520261667250, 7359761551432161, 8192683066856378, 9119868268136230, 10151985198186376, 11300909227415580, 12579859689817292, 14003551982487792, 15588366878604342, 17352539001951086, 19316366631550092, 21502445250375680, 23935927525325748, 26644812709737600, 29660268798266784, 33016991140790860, 36753601641491664, 40913093136236104, 45543324061189736, 50697569104240168, 56435132174936472, 62822028745677552, 69931745415056768, 77846085432775824, 86656109914600688, 96463185576826656, 107380151045315664, 119532615158469088, 133060402202199856, 148119160705543712, 164882154307451552, 183542255300186560, 204314163786713728, 227436877985347776, 253176444104585088, 281829017427734464, 313724269827691328, 349229182918168832, 388752270484770624, 432748278778513664, 481723418752617984, 536241190443833600, 596928866512693376, 664484709541257600, 739686006129409280, 823398010228713984, 916583898614395264, 1020315853041475584, 1135787396594579584, 1264327126171442688, 1407413999103859968, 1566694349801462272, 1744000832209069824, 1941373506026471680, 2161083309305266176, 2405658187494662656, 2677912179572818944, 2980977795924034048, 3318342060496414208, 3693886631935247360, 4111932465319354368, 4577289528371127808, 5095312144166932480, 5671960597112134656, 6313869711009142784, 7028425188266614784, 7823848588596424704, 8709291924949524480, 9223372036854775807, 9223372036854775807, 9223372036854775807, 9223372036854775807, 9223372036854775807, 9223372036854775807, 9223372036854775807, ]; ``` The list of thresholds separating the various bags. Ids are separated into unsorted bags according to their score. This specifies the thresholds separating the bags. An id's bag is the largest bag for which the id's score is less than or equal to its upper threshold. When ids are iterated, higher bags are iterated completely before lower bags. This means that iteration is *semi-sorted*: ids of higher score tend to come before ids of lower score, but peer ids within a particular bag are sorted in insertion order. - Expressing the constant This constant must be sorted in strictly increasing order. Duplicate items are not permitted. There is an implied upper limit of `Score::MAX`; that value does not need to be specified within the bag. For any two threshold lists, if one ends with `Score::MAX`, the other one does not, and they are otherwise equal, the two lists will behave identically. - Calculation It is recommended to generate the set of thresholds in a geometric series, such that there exists some constant ratio such that `threshold[k + 1] == (threshold[k] * constant_ratio).max(threshold[k] + 1)` for all `k`. The helpers in the `/utils/frame/generate-bags` module can simplify this calculation. - Examples - If `BagThresholds::get().is_empty()`, then all ids are put into the same bag, and iteration is strictly in insertion order. - If `BagThresholds::get().len() == 64`, and the thresholds are determined according to the procedure given above, then the constant ratio is equal to 2. - If `BagThresholds::get().len() == 200`, and the thresholds are determined according to the procedure given above, then the constant ratio is approximately equal to 1.248. - If the threshold list begins `[1, 2, 3, ...]`, then an id with score 0 or 1 will fall into bag 0, an id with score 2 will fall into bag 1, etc. - Migration In the event that this list ever changes, a copy of the old bags list must be retained. With that `List::migrate` can be called, which will perform the appropriate migration. ### nominationPools #### palletId ```dart final _i24.PalletId palletId = const [ 112, 121, 47, 110, 111, 112, 108, 115, ]; ``` The nomination pool's pallet id. #### maxPointsToBalance ```dart final int maxPointsToBalance = 10; ``` The maximum pool points-to-balance ratio that an `open` pool can have. This is important in the event slashing takes place and the pool's points-to-balance ratio becomes disproportional. Moreover, this relates to the `RewardCounter` type as well, as the arithmetic operations are a function of number of points, and by setting this value to e.g. 10, you ensure that the total number of points in the system are at most 10 times the total\_issuance of the chain, in the absolute worse case. For a value of 10, the threshold would be a pool points-to-balance ratio of 10:1. Such a scenario would also be the equivalent of the pool being 90% slashed. #### maxUnbonding ```dart final int maxUnbonding = 32; ``` The maximum number of simultaneous unbonding chunks that can exist per member. ### fastUnstake #### deposit ```dart final BigInt deposit = BigInt.from(10000000000); ``` Deposit to take for unstaking, to make sure we're able to slash the it in order to cover the costs of resources on unsuccessful unstake. ### paras #### unsignedPriority ```dart final BigInt unsignedPriority = BigInt.parse( '18446744073709551615', radix: 10, ); ``` ### onDemand #### trafficDefaultValue ```dart final _i12.FixedU128 trafficDefaultValue = BigInt.parse( '1000000000000000000', radix: 10, ); ``` The default value for the spot traffic multiplier. #### maxHistoricalRevenue ```dart final int maxHistoricalRevenue = 160; ``` The maximum number of blocks some historical revenue information stored for. #### palletId ```dart final _i13.PalletId palletId = const [ 112, 121, 47, 111, 110, 100, 109, 100, ]; ``` Identifier for the internal revenue balance. ### registrar #### paraDeposit ```dart final BigInt paraDeposit = BigInt.from(1000000000000); ``` The deposit to be paid to run a on-demand parachain. This should include the cost for storing the genesis head and validation code. #### dataDepositPerByte ```dart final BigInt dataDepositPerByte = BigInt.from(10000000); ``` The deposit to be paid per byte stored on chain. ### slots #### leasePeriod ```dart final int leasePeriod = 1209600; ``` The number of blocks over which a single period lasts. #### leaseOffset ```dart final int leaseOffset = 921600; ``` The number of blocks to offset each lease period by. ### auctions #### endingPeriod ```dart final int endingPeriod = 72000; ``` The number of blocks over which an auction may be retroactively ended. #### sampleLength ```dart final int sampleLength = 20; ``` The length of each sample to take during the ending period. `EndingPeriod` / `SampleLength` = Total # of Samples #### slotRangeCount ```dart final int slotRangeCount = 36; ``` #### leasePeriodsPerSlot ```dart final int leasePeriodsPerSlot = 8; ``` ### crowdloan #### palletId ```dart final _i12.PalletId palletId = const [ 112, 121, 47, 99, 102, 117, 110, 100, ]; ``` `PalletId` for the crowdloan pallet. An appropriate value could be `PalletId(*b"py/cfund")` #### minContribution ```dart final BigInt minContribution = BigInt.from(50000000000); ``` The minimum amount that may be contributed into a crowdloan. Should almost certainly be at least `ExistentialDeposit`. #### removeKeysLimit ```dart final int removeKeysLimit = 1000; ``` Max number of storage keys to remove per extrinsic call. ### coretime #### brokerId ```dart final int brokerId = 1005; ``` The ParaId of the coretime chain. #### brokerPotLocation ```dart final _i6.Junctions brokerPotLocation = const _i6.X1([ _i7.AccountId32( network: null, id: [ 109, 111, 100, 108, 112, 121, 47, 98, 114, 111, 107, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], ) ]); ``` The coretime chain pot location. ### stateTrieMigration #### maxKeyLen ```dart final int maxKeyLen = 512; ``` Maximal number of bytes that a key can have. FRAME itself does not limit the key length. The concrete value must therefore depend on your storage usage. A `frame_support::storage::StorageNMap` for example can have an arbitrary number of keys which are then hashed and concatenated, resulting in arbitrarily long keys. Use the *state migration RPC* to retrieve the length of the longest key in your storage: The migration will halt with a `Halted` event if this value is too small. Since there is no real penalty from over-estimating, it is advised to use a large value. The default is 512 byte. Some key lengths for reference: - `frame_support::storage::StorageValue`: 32 byte - `frame_support::storage::StorageMap`: 64 byte - `frame_support::storage::StorageDoubleMap`: 96 byte ### messageQueue #### heapSize ```dart final int heapSize = 65536; ``` The size of the page; this implies the maximum message size which can be sent. A good value depends on the expected message sizes, their weights, the weight that is available for processing them and the maximal needed message size. The maximal message size is slightly lower than this as defined by `MaxMessageLenOf`. #### maxStale ```dart final int maxStale = 8; ``` The maximum number of stale pages (i.e. of overweight messages) allowed before culling can happen. Once there are more stale pages than this, then historical pages may be dropped, even if they contain unprocessed overweight messages. #### serviceWeight ```dart final _i10.Weight? serviceWeight = _i10.Weight( refTime: BigInt.from(400000000000), proofSize: BigInt.parse( '3689348814741910323', radix: 10, ), ); ``` The amount of weight (if any) which should be provided to the message queue for servicing enqueued items `on_initialize`. This may be legitimately `None` in the case that you will call `ServiceQueues::service_queues` manually or set `Self::IdleMaxServiceWeight` to have it run in `on_idle`. #### idleMaxServiceWeight ```dart final _i10.Weight? idleMaxServiceWeight = _i10.Weight( refTime: BigInt.from(400000000000), proofSize: BigInt.parse( '3689348814741910323', radix: 10, ), ); ``` The maximum amount of weight (if any) to be used from remaining weight `on_idle` which should be provided to the message queue for servicing enqueued items `on_idle`. Useful for parachains to process messages at the same block they are received. If `None`, it will not call `ServiceQueues::service_queues` in `on_idle`. ### beefy #### maxAuthorities ```dart final int maxAuthorities = 100000; ``` The maximum number of authorities that can be added. #### maxNominators ```dart final int maxNominators = 512; ``` The maximum number of nominators for each validator. #### maxSetIdSessionEntries ```dart final BigInt maxSetIdSessionEntries = BigInt.from(168); ``` The maximum number of entries to keep in the set id to session index mapping. Since the `SetIdSession` map is only used for validating equivocations this value should relate to the bonding duration of whatever staking system is being used (if any). If equivocation handling is not enabled then this value can be zero. # Extrinsics ## Reference ### system #### remark ```dart remark({required List remark}) ``` Make some on-chain remark. Can be executed by every `origin`. #### setHeapPages ```dart setHeapPages({required BigInt pages}) { ``` Set the number of pages in the WebAssembly environment's heap. #### setCode ```dart setCode({required List code}) { ``` Set the new runtime code. #### setCodeWithoutChecks ```dart setCodeWithoutChecks({required List code}) { ``` Set the new runtime code without doing any checks of the given `code`. Note that runtime upgrades will not run if this is called with a not-increasing spec version! #### setStorage ```dart setStorage( {required List<_i19.Tuple2, List>> items}) { ``` Set some items of storage. #### killStorage ```dart killStorage({required List> keys}) { ``` Kill some items from storage. #### killPrefix ```dart killPrefix({ required List prefix, required int subkeys, ``` Kill all storage items with a key that starts with the given prefix. **NOTE:** We rely on the Root origin to provide us the number of subkeys under the prefix we are removing to accurately calculate the weight of this function. #### remarkWithEvent ```dart remarkWithEvent({required List remark}) { ``` Make some on-chain remark and emit event. #### authorizeUpgrade ```dart authorizeUpgrade({required _i6.H256 codeHash}) { ``` Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied later. This call requires Root origin. #### authorizeUpgradeWithoutChecks ```dart authorizeUpgradeWithoutChecks({required _i6.H256 codeHash}) { ``` Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied later. WARNING: This authorizes an upgrade that will take place without any safety checks, for example that the spec name remains the same and that the version number increases. Not recommended for normal use. Use `authorize_upgrade` instead. This call requires Root origin. #### applyAuthorizedUpgrade ```dart applyAuthorizedUpgrade({required List code}) { ``` Provide the preimage (runtime binary) `code` for an upgrade that has been authorized. If the authorization required a version check, this call will ensure the spec name remains unchanged and that the spec version has increased. Depending on the runtime's `OnSetCode` configuration, this function may directly apply the new `code` in the same block or attempt to schedule the upgrade. All origins are allowed. ### scheduler #### schedule ```dart schedule({ required int when, _i4.Tuple2? maybePeriodic, required int priority, required _i8.RuntimeCall call, ``` Anonymously schedule a task. #### cancel ```dart cancel({ required int when, required int index, ``` Cancel an anonymously scheduled task. #### scheduleNamed ```dart scheduleNamed({ required List id, required int when, _i4.Tuple2? maybePeriodic, required int priority, required _i8.RuntimeCall call, ``` Schedule a named task. #### cancelNamed ```dart cancelNamed({required List id}) { ``` Cancel a named scheduled task. #### scheduleAfter ```dart scheduleAfter({ required int after, _i4.Tuple2? maybePeriodic, required int priority, required _i8.RuntimeCall call, ``` Anonymously schedule a task after a delay. #### scheduleNamedAfter ```dart scheduleNamedAfter({ required List id, required int after, _i4.Tuple2? maybePeriodic, required int priority, required _i8.RuntimeCall call, ``` Schedule a named task after a delay. #### setRetry ```dart setRetry({ required _i4.Tuple2 task, required int retries, required int period, ``` Set a retry configuration for a task so that, in case its scheduled run fails, it will be retried after `period` blocks, for a total amount of `retries` retries or until it succeeds. Tasks which need to be scheduled for a retry are still subject to weight metering and agenda space, same as a regular task. If a periodic task fails, it will be scheduled normally while the task is retrying. Tasks scheduled as a result of a retry for a periodic task are unnamed, non-periodic clones of the original task. Their retry configuration will be derived from the original task's configuration, but will have a lower value for `remaining` than the original `total_retries`. #### setRetryNamed ```dart setRetryNamed({ required List id, required int retries, required int period, ``` Set a retry configuration for a named task so that, in case its scheduled run fails, it will be retried after `period` blocks, for a total amount of `retries` retries or until it succeeds. Tasks which need to be scheduled for a retry are still subject to weight metering and agenda space, same as a regular task. If a periodic task fails, it will be scheduled normally while the task is retrying. Tasks scheduled as a result of a retry for a periodic task are unnamed, non-periodic clones of the original task. Their retry configuration will be derived from the original task's configuration, but will have a lower value for `remaining` than the original `total_retries`. #### cancelRetry ```dart cancelRetry({required _i4.Tuple2 task}) { ``` Removes the retry configuration of a task. #### cancelRetryNamed ```dart cancelRetryNamed({required List id}) { ``` Cancel the retry configuration of a named task. ### preimage #### notePreimage ```dart notePreimage({required List bytes}) { ``` Register a preimage on-chain. If the preimage was previously requested, no fees or deposits are taken for providing the preimage. Otherwise, a deposit is taken proportional to the size of the preimage. #### unnotePreimage ```dart unnotePreimage({required _i2.H256 hash}) { ``` Clear an unrequested preimage from the runtime storage. If `len` is provided, then it will be a much cheaper operation. - `hash`: The hash of the preimage to be removed from the store. - `len`: The length of the preimage of `hash`. #### requestPreimage ```dart requestPreimage({required _i2.H256 hash}) { ``` Request a preimage be uploaded to the chain without paying any fees or deposits. If the preimage requests has already been provided on-chain, we unreserve any deposit a user may have paid, and take the control of the preimage out of their hands. #### unrequestPreimage ```dart unrequestPreimage({required _i2.H256 hash}) { ``` Clear a previously made request for a preimage. NOTE: THIS MUST NOT BE CALLED ON `hash` MORE TIMES THAN `request_preimage`. #### ensureUpdated ```dart ensureUpdated({required List<_i2.H256> hashes}) { ``` Ensure that the a bulk of pre-images is upgraded. The caller pays no fee if at least 90% of pre-images were successfully updated. ### babe #### reportEquivocation ```dart reportEquivocation({ required _i13.EquivocationProof equivocationProof, required _i14.MembershipProof keyOwnerProof, ``` Report authority equivocation/misbehavior. This method will verify the equivocation proof and validate the given key ownership proof against the extracted offender. If both are valid, the offence will be reported. #### reportEquivocationUnsigned ```dart reportEquivocationUnsigned({ required _i13.EquivocationProof equivocationProof, required _i14.MembershipProof keyOwnerProof, ``` Report authority equivocation/misbehavior. This method will verify the equivocation proof and validate the given key ownership proof against the extracted offender. If both are valid, the offence will be reported. This extrinsic must be called unsigned and it is expected that only block authors will call it (validated in `ValidateUnsigned`), as such if the block author is defined it will be defined as the equivocation reporter. #### planConfigChange ```dart planConfigChange( {required _i6.NextConfigDescriptor config}) { ``` Plan an epoch config change. The epoch config change is recorded and will be enacted on the next call to `enact_epoch_change`. The config will be activated one epoch after. Multiple calls to this method will replace any existing planned config change that had not been enacted yet. ### timestamp #### set ```dart set({required BigInt now}) { ``` Set the current time. This call should be invoked exactly once per block. It will panic at the finalization phase, if this call hasn't been invoked by that time. The timestamp should be greater than the previous one by the amount specified by [`Config::MinimumPeriod`]. The dispatch origin for this call must be *None*. This dispatch class is *Mandatory* to ensure it gets executed in the block. Be aware that changing the complexity of this call could result exhausting the resources in a block to execute any other calls. - Complexity - `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`) - 1 storage read and 1 storage mutation (codec `O(1)` because of `DidUpdate::take` in `on_finalize`) - 1 event handler `on_timestamp_set`. Must be `O(1)`. ### indices #### claim ```dart claim({required int index}) { ``` Assign an previously unassigned index. Payment: `Deposit` is reserved from the sender account. The dispatch origin for this call must be *Signed*. - `index`: the index to be claimed. This must not be in use. Emits `IndexAssigned` if successful. - Complexity - `O(1)`. #### transfer ```dart transfer({ required _i9.MultiAddress new_, required int index, ``` Assign an index already owned by the sender to another account. The balance reservation is effectively transferred to the new account. The dispatch origin for this call must be *Signed*. - `index`: the index to be re-assigned. This must be owned by the sender. - `new`: the new owner of the index. This function is a no-op if it is equal to sender. Emits `IndexAssigned` if successful. - Complexity - `O(1)`. #### free ```dart free({required int index}) { ``` Free up an index owned by the sender. Payment: Any previous deposit placed for the index is unreserved in the sender account. The dispatch origin for this call must be *Signed* and the sender must own the index. - `index`: the index to be freed. This must be owned by the sender. Emits `IndexFreed` if successful. - Complexity - `O(1)`. #### forceTransfer ```dart forceTransfer({ required _i9.MultiAddress new_, required int index, required bool freeze, ``` Force an index to an account. This doesn't require a deposit. If the index is already held, then any deposit is reimbursed to its current owner. The dispatch origin for this call must be *Root*. - `index`: the index to be (re-)assigned. - `new`: the new owner of the index. This function is a no-op if it is equal to sender. - `freeze`: if set to `true`, will freeze the index so it cannot be transferred. Emits `IndexAssigned` if successful. - Complexity - `O(1)`. #### freeze ```dart freeze({required int index}) { ``` Freeze an index so it will always point to the sender account. This consumes the deposit. The dispatch origin for this call must be *Signed* and the signing account must have a non-frozen account `index`. - `index`: the index to be frozen in place. Emits `IndexFrozen` if successful. - Complexity - `O(1)`. ### balances #### transferAllowDeath ```dart transferAllowDeath({ required _i12.MultiAddress dest, required BigInt value, ``` Transfer some liquid free balance to another account. `transfer_allow_death` will set the `FreeBalance` of the sender and receiver. If the sender's account is below the existential deposit as a result of the transfer, the account will be reaped. The dispatch origin for this call must be `Signed` by the transactor. #### forceTransfer ```dart forceTransfer({ required _i12.MultiAddress source, required _i12.MultiAddress dest, required BigInt value, ``` Exactly as `transfer_allow_death`, except the origin must be root and the source account may be specified. #### transferKeepAlive ```dart transferKeepAlive({ required _i12.MultiAddress dest, required BigInt value, ``` Same as the [`transfer_allow_death`] call, but with a check that the transfer will not kill the origin account. 99% of the time you want [`transfer_allow_death`] instead. #### transferAll ```dart transferAll({ required _i12.MultiAddress dest, required bool keepAlive, ``` Transfer the entire transferable balance from the caller account. NOTE: This function only attempts to transfer *transferable* balances. This means that any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be transferred by this function. To ensure that this function results in a killed account, you might need to prepare the account by removing any reference counters, storage deposits, etc... The dispatch origin of this call must be Signed. - `dest`: The recipient of the transfer. - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all of the funds the account has, causing the sender account to be killed (false), or transfer everything except at least the existential deposit, which will guarantee to keep the sender account alive (true). #### forceUnreserve ```dart forceUnreserve({ required _i12.MultiAddress who, required BigInt amount, ``` Unreserve some balance from a user by force. Can only be called by ROOT. #### upgradeAccounts ```dart upgradeAccounts({required List<_i3.AccountId32> who}) { ``` Upgrade a specified account. - `origin`: Must be `Signed`. - `who`: The account to be upgraded. This will waive the transaction fee if at least all but 10% of the accounts needed to be upgraded. (We let some not have to be upgraded just in order to allow for the possibility of churn). #### forceSetBalance ```dart forceSetBalance({ required _i12.MultiAddress who, required BigInt newFree, ``` Set the regular balance of a given account. The dispatch origin for this call is `root`. #### forceAdjustTotalInssuance # Example ```dart forceAdjustTotalIssuance({ required _i14.AdjustmentDirection direction, required BigInt delta, ``` Adjust the total issuance in a saturating way. Can only be called by root and always needs a positive `delta`. #### burn ```dart burn({ required BigInt value, required bool keepAlive, ``` Burn the specified liquid free balance from the origin account. If the origin's account ends up below the existential deposit as a result of the burn and `keep_alive` is false, the account will be reaped. Unlike sending funds to a *burn* address, which merely makes the funds inaccessible, this `burn` operation will reduce total issuance by the amount *burned*. ### staking #### bond ```dart bond({ required BigInt value, required _i6.RewardDestination payee, ``` Take the origin account as a stash and lock up `value` of its balance. `controller` will be the account that controls it. `value` must be more than the `minimum_balance` specified by `T::Currency`. The dispatch origin for this call must be *Signed* by the stash account. Emits `Bonded`. - Complexity - Independent of the arguments. Moderate complexity. - O(1). - Three extra DB entries. NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are *never* cleaned unless the `origin` falls below *existential deposit* (or equal to 0) and gets removed as dust. #### bondExtra ```dart bondExtra({required BigInt maxAdditional}) { ``` Add some extra amount that have appeared in the stash `free_balance` into the balance up for staking. The dispatch origin for this call must be *Signed* by the stash, not the controller. Use this if there are additional funds in your stash account that you wish to bond. Unlike [`bond`](Self::bond) or [`unbond`](Self::unbond) this function does not impose any limitation on the amount that can be added. Emits `Bonded`. - Complexity - Independent of the arguments. Insignificant complexity. - O(1). #### unbond ```dart unbond({required BigInt value}) { ``` Schedule a portion of the stash to be unlocked ready for transfer out after the bond period ends. If this leaves an amount actively bonded less than T::Currency::minimum\_balance(), then it is increased to the full amount. The dispatch origin for this call must be *Signed* by the controller, not the stash. Once the unlock period is done, you can call `withdraw_unbonded` to actually move the funds out of management ready for transfer. No more than a limited number of unlocking chunks (see `MaxUnlockingChunks`) can co-exists at the same time. If there are no unlocking chunks slots available [`Call::withdraw_unbonded`] is called to remove some of the chunks (if possible). If a user encounters the `InsufficientBond` error when calling this extrinsic, they should call `chill` first in order to free up their bonded funds. Emits `Unbonded`. See also [`Call::withdraw_unbonded`]. #### withdrawUnbonded ```dart withdrawUnbonded({required int numSlashingSpans}) { ``` Remove any unlocked chunks from the `unlocking` queue from our management. This essentially frees up that balance to be used by the stash account to do whatever it wants. The dispatch origin for this call must be *Signed* by the controller. Emits `Withdrawn`. See also [`Call::unbond`]. - Parameters - `num_slashing_spans` indicates the number of metadata slashing spans to clear when this call results in a complete removal of all the data related to the stash account. In this case, the `num_slashing_spans` must be larger or equal to the number of slashing spans associated with the stash account in the [`SlashingSpans`] storage type, otherwise the call will fail. The call weight is directly proportional to `num_slashing_spans`. - Complexity O(S) where S is the number of slashing spans to remove NOTE: Weight annotation is the kill scenario, we refund otherwise. #### validate ```dart validate({required _i7.ValidatorPrefs prefs}) { ``` Declare the desire to validate for the origin controller. Effects will be felt at the beginning of the next era. The dispatch origin for this call must be *Signed* by the controller, not the stash. #### nominate ```dart nominate({required List<_i25.MultiAddress> targets}) { ``` Declare the desire to nominate `targets` for the origin controller. Effects will be felt at the beginning of the next era. The dispatch origin for this call must be *Signed* by the controller, not the stash. - Complexity - The transaction's complexity is proportional to the size of `targets` (N) which is capped at CompactAssignments::LIMIT (T::MaxNominations). - Both the reads and writes follow a similar pattern. #### chill ```dart chill() { ``` Declare no desire to either validate or nominate. Effects will be felt at the beginning of the next era. The dispatch origin for this call must be *Signed* by the controller, not the stash. - Complexity - Independent of the arguments. Insignificant complexity. - Contains one read. - Writes are limited to the `origin` account key. #### setPayee ```dart setPayee({required _i6.RewardDestination payee}) { ``` (Re-)set the payment target for a controller. Effects will be felt instantly (as soon as this function is completed successfully). The dispatch origin for this call must be *Signed* by the controller, not the stash. - Complexity - O(1) - Independent of the arguments. Insignificant complexity. - Contains a limited number of reads. - Writes are limited to the `origin` account key. #### setController ```dart setController() { ``` (Re-)sets the controller of a stash to the stash itself. This function previously accepted a `controller` argument to set the controller to an account other than the stash itself. This functionality has now been removed, now only setting the controller to the stash, if it is not already. Effects will be felt instantly (as soon as this function is completed successfully). The dispatch origin for this call must be *Signed* by the stash, not the controller. - Complexity O(1) - Independent of the arguments. Insignificant complexity. - Contains a limited number of reads. - Writes are limited to the `origin` account key. #### setValidatorCount ```dart setValidatorCount({required BigInt new_}) { ``` Sets the ideal number of validators. The dispatch origin must be Root. - Complexity O(1) #### increaseValidatorCount ```dart increaseValidatorCount({required BigInt additional}) { ``` Increments the ideal number of validators up to maximum of `ElectionProviderBase::MaxWinners`. The dispatch origin must be Root. - Complexity Same as [`Self::set_validator_count`]. #### scaleValidatorCount ```dart scaleValidatorCount({required _i15.Percent factor}) { ``` Scale up the ideal number of validators by a factor up to maximum of `ElectionProviderBase::MaxWinners`. The dispatch origin must be Root. - Complexity Same as [`Self::set_validator_count`]. #### forceNoEras ```dart forceNoEras() { ``` Force there to be no new eras indefinitely. The dispatch origin must be Root. # Warning The election process starts multiple blocks before the end of the era. Thus the election process may be ongoing when this is called. In this case the election will continue until the next era is triggered. - Complexity - No arguments. - Weight: O(1) #### forceNewEra ```dart forceNewEra() { ``` Force there to be a new era at the end of the next session. After this, it will be reset to normal (non-forced) behaviour. The dispatch origin must be Root. # Warning The election process starts multiple blocks before the end of the era. If this is called just before a new era is triggered, the election process may not have enough blocks to get a result. - Complexity - No arguments. - Weight: O(1) #### setInvulnerables ```dart setInvulnerables( {required List<_i3.AccountId32> invulnerables}) { ``` Set the validators who cannot be slashed (if any). The dispatch origin must be Root. #### forceUnstake ```dart forceUnstake({ required _i3.AccountId32 stash, required int numSlashingSpans, ``` Force a current staker to become completely unstaked, immediately. The dispatch origin must be Root. - Parameters - `num_slashing_spans`: Refer to comments on [`Call::withdraw_unbonded`] for more details. #### forceNewEraAlways ```dart forceNewEraAlways() { ``` Force there to be a new era at the end of sessions indefinitely. The dispatch origin must be Root. # Warning The election process starts multiple blocks before the end of the era. If this is called just before a new era is triggered, the election process may not have enough blocks to get a result. #### cancelDeferredSlash ```dart cancelDeferredSlash({ required int era, required List slashIndices, ``` Cancel enactment of a deferred slash. Can be called by the `T::AdminOrigin`. Parameters: era and indices of the slashes for that era to kill. #### payoutStakers ```dart payoutStakers({ required _i3.AccountId32 validatorStash, required int era, ``` Pay out next page of the stakers behind a validator for the given era. - `validator_stash` is the stash account of the validator. - `era` may be any era between `[current_era - history_depth; current_era]`. The origin of this call must be *Signed*. Any account can call this function, even if it is not one of the stakers. The reward payout could be paged in case there are too many nominators backing the `validator_stash`. This call will payout unpaid pages in an ascending order. To claim a specific page, use `payout_stakers_by_page`.\` If all pages are claimed, it returns an error `InvalidPage`. #### rebond ```dart rebond({required BigInt value}) { ``` Rebond a portion of the stash scheduled to be unlocked. The dispatch origin must be signed by the controller. - Complexity - Time complexity: O(L), where L is unlocking chunks - Bounded by `MaxUnlockingChunks`. #### reapStash ```dart reapStash({ required _i3.AccountId32 stash, required int numSlashingSpans, ``` Remove all data structures concerning a staker/stash once it is at a state where it can be considered `dust` in the staking system. The requirements are: 1. the `total_balance` of the stash is below existential deposit. 2. or, the `ledger.total` of the stash is below existential deposit. 3. or, existential deposit is zero and either `total_balance` or `ledger.total` is zero. The former can happen in cases like a slash; the latter when a fully unbonded account is still receiving staking rewards in `RewardDestination::Staked`. It can be called by anyone, as long as `stash` meets the above requirements. Refunds the transaction fees upon successful execution. - Parameters - `num_slashing_spans`: Refer to comments on [`Call::withdraw_unbonded`] for more details. #### kick ```dart kick({required List<_i25.MultiAddress> who}) { ``` Remove the given nominations from the calling validator. Effects will be felt at the beginning of the next era. The dispatch origin for this call must be *Signed* by the controller, not the stash. - `who`: A list of nominator stash accounts who are nominating this validator which should no longer be nominating this validator. Note: Making this call only makes sense if you first set the validator preferences to block any further nominations. #### setStakingConfigs ```dart setStakingConfigs({ required _i26.ConfigOp minNominatorBond, required _i26.ConfigOp minValidatorBond, required _i27.ConfigOp maxNominatorCount, required _i27.ConfigOp maxValidatorCount, required _i28.ConfigOp chillThreshold, required _i29.ConfigOp minCommission, required _i28.ConfigOp maxStakedRewards, ``` Update the various staking configurations . - `min_nominator_bond`: The minimum active bond needed to be a nominator. - `min_validator_bond`: The minimum active bond needed to be a validator. - `max_nominator_count`: The max number of users who can be a nominator at once. When set to `None`, no limit is enforced. - `max_validator_count`: The max number of users who can be a validator at once. When set to `None`, no limit is enforced. - `chill_threshold`: The ratio of `max_nominator_count` or `max_validator_count` which should be filled in order for the `chill_other` transaction to work. - `min_commission`: The minimum amount of commission that each validators must maintain. This is checked only upon calling `validate`. Existing validators are not affected. RuntimeOrigin must be Root to call this function. NOTE: Existing nominators and validators will not be affected by this update. to kick people under the new limits, `chill_other` should be called. #### chillOther ```dart chillOther({required _i3.AccountId32 stash}) { ``` Declare a `controller` to stop participating as either a validator or nominator. Effects will be felt at the beginning of the next era. The dispatch origin for this call must be *Signed*, but can be called by anyone. If the caller is the same as the controller being targeted, then no further checks are enforced, and this function behaves just like `chill`. If the caller is different than the controller being targeted, the following conditions must be met: - `controller` must belong to a nominator who has become non-decodable, Or: - A `ChillThreshold` must be set and checked which defines how close to the max nominators or validators we must reach before users can start chilling one-another. - A `MaxNominatorCount` and `MaxValidatorCount` must be set which is used to determine how close we are to the threshold. - A `MinNominatorBond` and `MinValidatorBond` must be set and checked, which determines if this is a person that should be chilled because they have not met the threshold bond required. This can be helpful if bond requirements are updated, and we need to remove old users who do not satisfy these requirements. #### forceApplyMinCommission ```dart forceApplyMinCommission( {required _i3.AccountId32 validatorStash}) { ``` Force a validator to have at least the minimum commission. This will not affect a validator who already has a commission greater than or equal to the minimum. Any account can call this. #### setMinCommission ```dart setMinCommission({required _i4.Perbill new_}) { ``` Sets the minimum amount of commission that each validators must maintain. This call has lower privilege requirements than `set_staking_config` and can be called by the `T::AdminOrigin`. Root can always call this. #### payoutStakersByPage ```dart payoutStakersByPage({ required _i3.AccountId32 validatorStash, required int era, required int page, ``` Pay out a page of the stakers behind a validator for the given era and page. - `validator_stash` is the stash account of the validator. - `era` may be any era between `[current_era - history_depth; current_era]`. - `page` is the page index of nominators to pay out with value between 0 and `num_nominators / T::MaxExposurePageSize`. The origin of this call must be *Signed*. Any account can call this function, even if it is not one of the stakers. If a validator has more than [`Config::MaxExposurePageSize`] nominators backing them, then the list of nominators is paged, with each page being capped at [`Config::MaxExposurePageSize`.] If a validator has more than one page of nominators, the call needs to be made for each page separately in order for all the nominators backing a validator to receive the reward. The nominators are not sorted across pages and so it should not be assumed the highest staker would be on the topmost page and vice versa. If rewards are not claimed in [`Config::HistoryDepth`] eras, they are lost. #### updatePayee ```dart updatePayee({required _i3.AccountId32 controller}) { ``` Migrates an account's `RewardDestination::Controller` to `RewardDestination::Account(controller)`. Effects will be felt instantly (as soon as this function is completed successfully). This will waive the transaction fee if the `payee` is successfully migrated. #### deprecateControllerBatch ```dart deprecateControllerBatch( {required List<_i3.AccountId32> controllers}) { ``` Updates a batch of controller accounts to their corresponding stash account if they are not the same. Ignores any controller accounts that do not exist, and does not operate if the stash and controller are already the same. Effects will be felt instantly (as soon as this function is completed successfully). The dispatch origin must be `T::AdminOrigin`. #### restoreLedger ```dart restoreLedger({ required _i3.AccountId32 stash, _i3.AccountId32? maybeController, BigInt? maybeTotal, List<_i30.UnlockChunk>? maybeUnlocking, ``` Restores the state of a ledger which is in an inconsistent state. The requirements to restore a ledger are the following: - The stash is bonded; or - The stash is not bonded but it has a staking lock left behind; or - If the stash has an associated ledger and its state is inconsistent; or - If the ledger is not corrupted *but* its staking lock is out of sync. The `maybe_*` input parameters will overwrite the corresponding data and metadata of the ledger associated with the stash. If the input parameters are not set, the ledger will be reset values from on-chain state. ### session #### setKeys ```dart setKeys({ required _i5.SessionKeys keys, required List proof, ``` Sets the session key(s) of the function caller to `keys`. Allows an account to set its session key prior to becoming a validator. This doesn't take effect until the next session. The dispatch origin of this function must be signed. - Complexity - `O(1)`. Actual cost depends on the number of length of `T::Keys::key_ids()` which is fixed. #### purgeKeys ```dart purgeKeys() { ``` Removes any session key(s) of the function caller. This doesn't take effect until the next session. The dispatch origin of this function must be Signed and the account must be either be convertible to a validator ID using the chain's typical addressing system (this usually means being a controller account) or directly convertible into a validator ID (which usually means being a stash account). - Complexity - `O(1)` in number of key types. Actual cost depends on the number of length of `T::Keys::key_ids()` which is fixed. ### grandpa #### reportEquivocation ```dart reportEquivocation({ required _i11.EquivocationProof equivocationProof, required _i12.MembershipProof keyOwnerProof, ``` Report voter equivocation/misbehavior. This method will verify the equivocation proof and validate the given key ownership proof against the extracted offender. If both are valid, the offence will be reported. #### reportEquivocationUnsigned ```dart reportEquivocationUnsigned({ required _i11.EquivocationProof equivocationProof, required _i12.MembershipProof keyOwnerProof, ``` Report voter equivocation/misbehavior. This method will verify the equivocation proof and validate the given key ownership proof against the extracted offender. If both are valid, the offence will be reported. This extrinsic must be called unsigned and it is expected that only block authors will call it (validated in `ValidateUnsigned`), as such if the block author is defined it will be defined as the equivocation reporter. #### noteStalled ```dart noteStalled({ required int delay, required int bestFinalizedBlockNumber, ``` Note that the current authority set of the GRANDPA finality gadget has stalled. This will trigger a forced authority set change at the beginning of the next session, to be enacted `delay` blocks after that. The `delay` should be high enough to safely assume that the block signalling the forced change will not be re-orged e.g. 1000 blocks. The block production rate (which may be slowed down because of finality lagging) should be taken into account when choosing the `delay`. The GRANDPA voters based on the new authority will start voting on top of `best_finalized_block_number` for new finalized blocks. `best_finalized_block_number` should be the highest of the latest finalized block of all validators of the new authority set. Only callable by root. ### treasury #### spendLocal ```dart spendLocal({ required BigInt amount, required _i8.MultiAddress beneficiary, ``` Propose and approve a spend of treasury funds. - Dispatch Origin Must be [`Config::SpendOrigin`] with the `Success` value being at least `amount`. \#- Details NOTE: For record-keeping purposes, the proposer is deemed to be equivalent to the beneficiary. \#- Parameters - `amount`: The amount to be transferred from the treasury to the `beneficiary`. - `beneficiary`: The destination account for the transfer. - Events Emits [`Event::SpendApproved`] if successful. #### removeApproval ```dart removeApproval({required BigInt proposalId}) { ``` Force a previously approved proposal to be removed from the approval queue. - Dispatch Origin Must be [`Config::RejectOrigin`]. - Details The original deposit will no longer be returned. \#- Parameters - `proposal_id`: The index of a proposal \#- Complexity - O(A) where `A` is the number of approvals ## Errors - [`Error::ProposalNotApproved`]: The `proposal_id` supplied was not found in the approval queue, i.e., the proposal has not been approved. This could also mean the proposal does not exist altogether, thus there is no way it would have been approved in the first place. #### spend ```dart spend({ required _i10.VersionedLocatableAsset assetKind, required BigInt amount, required _i11.VersionedLocation beneficiary, int? validFrom, ``` Propose and approve a spend of treasury funds. - Dispatch Origin Must be [`Config::SpendOrigin`] with the `Success` value being at least `amount` of `asset_kind` in the native asset. The amount of `asset_kind` is converted for assertion using the [`Config::BalanceConverter`]. - Details Create an approved spend for transferring a specific `amount` of `asset_kind` to a designated beneficiary. The spend must be claimed using the `payout` dispatchable within the [`Config::PayoutPeriod`]. \#- Parameters - `asset_kind`: An indicator of the specific asset class to be spent. - `amount`: The amount to be transferred from the treasury to the `beneficiary`. - `beneficiary`: The beneficiary of the spend. - `valid_from`: The block number from which the spend can be claimed. It can refer to the past if the resulting spend has not yet expired according to the [`Config::PayoutPeriod`]. If `None`, the spend can be claimed immediately after approval. - Events Emits [`Event::AssetSpendApproved`] if successful. #### payout ```dart payout({required int index}) { ``` Claim a spend. - Dispatch Origin Must be signed - Details Spends must be claimed within some temporal bounds. A spend may be claimed within one [`Config::PayoutPeriod`] from the `valid_from` block. In case of a payout failure, the spend status must be updated with the `check_status` dispatchable before retrying with the current function. \#- Parameters - `index`: The spend index. - Events Emits [`Event::Paid`] if successful. #### checkStatus ```dart checkStatus({required int index}) { ``` Check the status of the spend and remove it from the storage if processed. - Dispatch Origin Must be signed. - Details The status check is a prerequisite for retrying a failed payout. If a spend has either succeeded or expired, it is removed from the storage by this function. In such instances, transaction fees are refunded. \#- Parameters - `index`: The spend index. - Events Emits [`Event::PaymentFailed`] if the spend payout has failed. Emits [`Event::SpendProcessed`] if the spend payout has succeed. #### voidSpend ```dart voidSpend({required int index}) { ``` Void previously approved spend. - Dispatch Origin Must be [`Config::RejectOrigin`]. - Details A spend void is only possible if the payout has not been attempted yet. \#- Parameters - `index`: The spend index. - Events Emits [`Event::AssetSpendVoided`] if successful. ### convictionVoting #### vote ```dart vote({ required BigInt pollIndex, required _i12.AccountVote vote, ``` Vote in a poll. If `vote.is_aye()`, the vote is to enact the proposal; otherwise it is a vote to keep the status quo. The dispatch origin of this call must be *Signed*. - `poll_index`: The index of the poll to vote for. - `vote`: The vote configuration. Weight: `O(R)` where R is the number of polls the voter has voted on. #### delegate ```dart delegate({ required int class_, required _i14.MultiAddress to, required _i15.Conviction conviction, required BigInt balance, ``` Delegate the voting power (with some given conviction) of the sending account for a particular class of polls. The balance delegated is locked for as long as it's delegated, and thereafter for the time appropriate for the conviction's lock period. The dispatch origin of this call must be *Signed*, and the signing account must either: - be delegating already; or - have no voting activity (if there is, then it will need to be removed through `remove_vote`). - `to`: The account whose voting the `target` account's voting power will follow. - `class`: The class of polls to delegate. To delegate multiple classes, multiple calls to this function are required. - `conviction`: The conviction that will be attached to the delegated votes. When the account is undelegated, the funds will be locked for the corresponding period. - `balance`: The amount of the account's balance to be used in delegating. This must not be more than the account's current balance. Emits `Delegated`. Weight: `O(R)` where R is the number of polls the voter delegating to has voted on. Weight is initially charged as if maximum votes, but is refunded later. #### undelegate ```dart undelegate({required int class_}) { ``` Undelegate the voting power of the sending account for a particular class of polls. Tokens may be unlocked following once an amount of time consistent with the lock period of the conviction with which the delegation was issued has passed. The dispatch origin of this call must be *Signed* and the signing account must be currently delegating. - `class`: The class of polls to remove the delegation from. Emits `Undelegated`. Weight: `O(R)` where R is the number of polls the voter delegating to has voted on. Weight is initially charged as if maximum votes, but is refunded later. #### unlock ```dart unlock({ required int class_, required _i14.MultiAddress target, ``` Remove the lock caused by prior voting/delegating which has expired within a particular class. The dispatch origin of this call must be *Signed*. - `class`: The class of polls to unlock. - `target`: The account to remove the lock on. Weight: `O(R)` with R number of vote of target. #### removeVote ```dart removeVote({ int? class_, required int index, ``` Remove a vote for a poll. If: - the poll was cancelled, or - the poll is ongoing, or - the poll has ended such that - the vote of the account was in opposition to the result; or - there was no conviction to the account's vote; or - the account made a split vote ...then the vote is removed cleanly and a following call to `unlock` may result in more funds being available. If, however, the poll has ended and: - it finished corresponding to the vote of the account, and - the account made a standard vote with conviction, and - the lock period of the conviction is not over ...then the lock will be aggregated into the overall account's lock, which may involve *overlocking* (where the two locks are combined into a single lock that is the maximum of both the amount locked and the time is it locked for). The dispatch origin of this call must be *Signed*, and the signer must have a vote registered for poll `index`. - `index`: The index of poll of the vote to be removed. - `class`: Optional parameter, if given it indicates the class of the poll. For polls which have finished or are cancelled, this must be `Some`. Weight: `O(R + log R)` where R is the number of polls that `target` has voted on. Weight is calculated for the maximum number of vote. #### removeOtherVote ```dart removeOtherVote({ required _i14.MultiAddress target, required int class_, required int index, ``` Remove a vote for a poll. If the `target` is equal to the signer, then this function is exactly equivalent to `remove_vote`. If not equal to the signer, then the vote must have expired, either because the poll was cancelled, because the voter lost the poll or because the conviction period is over. The dispatch origin of this call must be *Signed*. - `target`: The account of the vote to be removed; this account must have voted for poll `index`. - `index`: The index of poll of the vote to be removed. - `class`: The class of the poll. Weight: `O(R + log R)` where R is the number of polls that `target` has voted on. Weight is calculated for the maximum number of vote. ### referenda #### submit ```dart submit({ required _i9.OriginCaller proposalOrigin, required _i10.Bounded proposal, required _i11.DispatchTime enactmentMoment, ``` Propose a referendum on a privileged action. - `origin`: must be `SubmitOrigin` and the account must have `SubmissionDeposit` funds available. - `proposal_origin`: The origin from which the proposal should be executed. - `proposal`: The proposal. - `enactment_moment`: The moment that the proposal should be enacted. Emits `Submitted`. #### placeDecisionDeposit ```dart placeDecisionDeposit({required int index}) { ``` Post the Decision Deposit for a referendum. - `origin`: must be `Signed` and the account must have funds available for the referendum's track's Decision Deposit. - `index`: The index of the submitted referendum whose Decision Deposit is yet to be posted. Emits `DecisionDepositPlaced`. #### refundDecisionDeposit ```dart refundDecisionDeposit({required int index}) { ``` Refund the Decision Deposit for a closed referendum back to the depositor. - `origin`: must be `Signed` or `Root`. - `index`: The index of a closed referendum whose Decision Deposit has not yet been refunded. Emits `DecisionDepositRefunded`. #### cancel ```dart cancel({required int index}) { ``` Cancel an ongoing referendum. - `origin`: must be the `CancelOrigin`. - `index`: The index of the referendum to be cancelled. Emits `Cancelled`. #### kill ```dart kill({required int index}) { ``` Cancel an ongoing referendum and slash the deposits. - `origin`: must be the `KillOrigin`. - `index`: The index of the referendum to be cancelled. Emits `Killed` and `DepositSlashed`. #### nudgeReferendum ```dart nudgeReferendum({required int index}) { ``` Advance a referendum onto its next logical state. Only used internally. - `origin`: must be `Root`. - `index`: the referendum to be advanced. #### oneFewerDeciding ```dart oneFewerDeciding({required int track}) { ``` Advance a track onto its next logical state. Only used internally. - `origin`: must be `Root`. - `track`: the track to be advanced. Action item for when there is now one fewer referendum in the deciding phase and the `DecidingCount` is not yet updated. This means that we should either: - begin deciding another referendum (and leave `DecidingCount` alone); or - decrement `DecidingCount`. #### refundSubmissionDeposit ```dart refundSubmissionDeposit({required int index}) { ``` Refund the Submission Deposit for a closed referendum back to the depositor. - `origin`: must be `Signed` or `Root`. - `index`: The index of a closed referendum whose Submission Deposit has not yet been refunded. Emits `SubmissionDepositRefunded`. #### setMetadata ```dart setMetadata({ required int index, _i5.H256? maybeHash, ``` Set or clear metadata of a referendum. Parameters: - `origin`: Must be `Signed` by a creator of a referendum or by anyone to clear a metadata of a finished referendum. - `index`: The index of a referendum to set or clear metadata for. - `maybe_hash`: The hash of an on-chain stored preimage. `None` to clear a metadata. ### whitelist #### whitelistCall ```dart whitelistCall({required _i2.H256 callHash}) { ``` #### removeWhitelistedCall ```dart removeWhitelistedCall({required _i2.H256 callHash}) { ``` #### dispatchWhitelistedCall ```dart dispatchWhitelistedCall({ required _i2.H256 callHash, required int callEncodedLen, required _i8.Weight callWeightWitness, ``` #### dispatchWhitelistedCallWithPreimage ```dart dispatchWhitelistedCallWithPreimage( {required _i6.RuntimeCall call}) { ``` ### claims #### claim ```dart claim({ required _i6.AccountId32 dest, required _i10.EcdsaSignature ethereumSignature, final _call = _i11.Call.values.claim( dest: dest, ethereumSignature: ethereumSignature, ); return _i9.RuntimeCall.values.claims(_call); ``` Make a claim to collect your DOTs. The dispatch origin for this call must be *None*. Unsigned Validation: A call to claim is deemed valid if the signature provided matches the expected signed message of: > Ethereum Signed Message: > (configured prefix string)(address) and `address` matches the `dest` account. Parameters: - `dest`: The destination account to payout the claim. - `ethereum_signature`: The signature of an ethereum signed message matching the format described above. ::weight The weight of this call is invariant over the input parameters. Weight includes logic to validate unsigned `claim` call. ```text Total Complexity: O(1) ``` :: #### mintClaim ```dart mintClaim({ required _i2.EthereumAddress who, required BigInt value, _i4.Tuple3? vestingSchedule, _i5.StatementKind? statement, final _call = _i11.Call.values.mintClaim( who: who, value: value, vestingSchedule: vestingSchedule, statement: statement, ); return _i9.RuntimeCall.values.claims(_call); ``` Mint a new claim to collect DOTs. The dispatch origin for this call must be *Root*. Parameters: - `who`: The Ethereum address allowed to collect this claim. - `value`: The number of DOTs that will be claimed. - `vesting_schedule`: An optional vesting schedule for these DOTs. ::weight The weight of this call is invariant over the input parameters. We assume worst case that both vesting and statement is being inserted. ```text Total Complexity: O(1) ``` :: #### claimAttest ```dart claimAttest({ required _i6.AccountId32 dest, required _i10.EcdsaSignature ethereumSignature, required List statement, ) { final _call = _i11.Call.values.claimAttest( dest: dest, ethereumSignature: ethereumSignature, statement: statement, ); return _i9.RuntimeCall.values.claims(_call); ``` Make a claim to collect your DOTs by signing a statement. The dispatch origin for this call must be *None*. Unsigned Validation: A call to `claim_attest` is deemed valid if the signature provided matches the expected signed message of: > Ethereum Signed Message: > (configured prefix string)(address)(statement) and `address` matches the `dest` account; the `statement` must match that which is expected according to your purchase arrangement. Parameters: - `dest`: The destination account to payout the claim. - `ethereum_signature`: The signature of an ethereum signed message matching the format described above. - `statement`: The identity of the statement which is being attested to in the signature. ::weight The weight of this call is invariant over the input parameters. Weight includes logic to validate unsigned `claim_attest` call. ```text Total Complexity: O(1) ``` :: #### attest ```dart attest({required List statement}) ``` Attest to a statement, needed to finalize the claims process. WARNING: Insecure unless your chain includes `PrevalidateAttests` as a `SignedExtension`. Unsigned Validation: A call to attest is deemed valid if the sender has a `Preclaim` registered and provides a `statement` which is expected for the account. Parameters: - `statement`: The identity of the statement which is being attested to in the signature. ::weight The weight of this call is invariant over the input parameters. Weight includes logic to do pre-validation on `attest` call. ```text Total Complexity: O(1) ``` :: #### moveClaim ```dart moveClaim({ required _i2.EthereumAddress old, required _i2.EthereumAddress new_, _i6.AccountId32? maybePreclaim, ``` ### vesting #### vest ```dart vest() { ``` Unlock any vested funds of the sender account. The dispatch origin for this call must be *Signed* and the sender must have funds still locked under this pallet. Emits either `VestingCompleted` or `VestingUpdated`. - Complexity - `O(1)`. #### vestOther ```dart vestOther({required _i10.MultiAddress target}) { ``` Unlock any vested funds of a `target` account. The dispatch origin for this call must be *Signed*. - `target`: The account whose vested funds should be unlocked. Must have funds still locked under this pallet. Emits either `VestingCompleted` or `VestingUpdated`. - Complexity - `O(1)`. #### vestedTransfer ```dart vestedTransfer({ required _i10.MultiAddress target, required _i3.VestingInfo schedule, ) { ``` Create a vested transfer. The dispatch origin for this call must be *Signed*. - `target`: The account receiving the vested funds. - `schedule`: The vesting schedule attached to the transfer. Emits `VestingCreated`. NOTE: This will unlock all schedules through the current block. - Complexity - `O(1)`. #### forceVestedTransfer ```dart forceVestedTransfer({ required _i10.MultiAddress source, required _i10.MultiAddress target, required _i3.VestingInfo schedule, ``` Force a vested transfer. The dispatch origin for this call must be *Root*. - `source`: The account whose funds should be transferred. - `target`: The account that should be transferred the vested funds. - `schedule`: The vesting schedule attached to the transfer. Emits `VestingCreated`. NOTE: This will unlock all schedules through the current block. - Complexity - `O(1)`. #### mergeSchedules ```dart mergeSchedules({ required int schedule1Index, required int schedule2Index, ``` Merge two vesting schedules together, creating a new vesting schedule that unlocks over the highest possible start and end blocks. If both schedules have already started the current block will be used as the schedule start; with the caveat that if one schedule is finished by the current block, the other will be treated as the new merged schedule, unmodified. NOTE: If `schedule1_index == schedule2_index` this is a no-op. NOTE: This will unlock all schedules through the current block prior to merging. NOTE: If both schedules have ended by the current block, no new schedule will be created and both will be removed. Merged schedule attributes: - `starting_block`: `MAX(schedule1.starting_block, scheduled2.starting_block, current_block)`. - `ending_block`: `MAX(schedule1.ending_block, schedule2.ending_block)`. - `locked`: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)`. The dispatch origin for this call must be *Signed*. - `schedule1_index`: index of the first schedule to merge. - `schedule2_index`: index of the second schedule to merge. #### forceRemoveVestingSchedule ```dart forceRemoveVestingSchedule({ required _i10.MultiAddress target, required int scheduleIndex, ``` Force remove a vesting schedule The dispatch origin for this call must be *Root*. - `target`: An account that has a vesting schedule - `schedule_index`: The vesting schedule index that should be removed ### utility #### batch ```dart batch({required List<_i1.RuntimeCall> calls}) { ``` Send a batch of dispatch calls. May be called from any origin except `None`. - `calls`: The calls to be dispatched from the same origin. The number of call must not exceed the constant: `batched_calls_limit` (available in constant metadata). If origin is root then the calls are dispatched without checking origin filter. (This includes bypassing `frame_system::Config::BaseCallFilter`). - Complexity - O(C) where C is the number of calls to be batched. This will return `Ok` in all circumstances. To determine the success of the batch, an event is deposited. If a call failed and the batch was interrupted, then the `BatchInterrupted` event is deposited, along with the number of successful calls made and the error of the failed call. If all were successful, then the `BatchCompleted` event is deposited. #### asDerivative ```dart asDerivative({ required int index, required _i1.RuntimeCall call, ``` Send a call through an indexed pseudonym of the sender. Filter from origin are passed along. The call will be dispatched with an origin which use the same filter as the origin of this call. NOTE: If you need to ensure that any account-based filtering is not honored (i.e. because you expect `proxy` to have been used prior in the call stack and you do not want the call restrictions to apply to any sub-accounts), then use `as_multi_threshold_1` in the Multisig pallet instead. NOTE: Prior to version \*12, this was called `as_limited_sub`. The dispatch origin for this call must be *Signed*. #### batchAll ```dart batchAll({required List<_i1.RuntimeCall> calls}) { ``` Send a batch of dispatch calls and atomically execute them. The whole transaction will rollback and fail if any of the calls failed. May be called from any origin except `None`. - `calls`: The calls to be dispatched from the same origin. The number of call must not exceed the constant: `batched_calls_limit` (available in constant metadata). If origin is root then the calls are dispatched without checking origin filter. (This includes bypassing `frame_system::Config::BaseCallFilter`). - Complexity - O(C) where C is the number of calls to be batched. #### dispatchAs ```dart dispatchAs({ required _i3.OriginCaller asOrigin, required _i1.RuntimeCall call, ``` Dispatches a function call with a provided origin. The dispatch origin for this call must be *Root*. - Complexity - O(1). #### forceBatch ```dart forceBatch({required List<_i1.RuntimeCall> calls}) { ``` Send a batch of dispatch calls. Unlike `batch`, it allows errors and won't interrupt. May be called from any origin except `None`. - `calls`: The calls to be dispatched from the same origin. The number of call must not exceed the constant: `batched_calls_limit` (available in constant metadata). If origin is root then the calls are dispatch without checking origin filter. (This includes bypassing `frame_system::Config::BaseCallFilter`). - Complexity - O(C) where C is the number of calls to be batched. #### withWeight ```dart withWeight({ required _i1.RuntimeCall call, required _i4.Weight weight, ``` Dispatch a function call with a specified weight. This function does not check the weight of the call, and instead allows the Root origin to specify the weight of the call. The dispatch origin for this call must be *Root*. ### proxy #### proxy ```dart proxy({ required _i10.MultiAddress real, _i11.ProxyType? forceProxyType, required _i9.RuntimeCall call, ``` Dispatch the given `call` from an account that the sender is authorised for through `add_proxy`. The dispatch origin for this call must be *Signed*. Parameters: - `real`: The account that the proxy will make a call on behalf of. - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call. - `call`: The call to be made by the `real` account. #### addProxy ```dart addProxy({ required _i10.MultiAddress delegate, required _i11.ProxyType proxyType, required int delay, ``` Register a proxy account for the sender that is able to make calls on its behalf. The dispatch origin for this call must be *Signed*. Parameters: - `proxy`: The account that the `caller` would like to make a proxy. - `proxy_type`: The permissions allowed for this proxy account. - `delay`: The announcement period required of the initial proxy. Will generally be zero. #### removeProxy ```dart removeProxy({ required _i10.MultiAddress delegate, required _i11.ProxyType proxyType, required int delay, ``` Unregister a proxy account for the sender. The dispatch origin for this call must be *Signed*. Parameters: - `proxy`: The account that the `caller` would like to remove as a proxy. - `proxy_type`: The permissions currently enabled for the removed proxy account. #### removeProxies ```dart removeProxies() { ``` Unregister all proxy accounts for the sender. The dispatch origin for this call must be *Signed*. WARNING: This may be called on accounts created by `pure`, however if done, then the unreserved fees will be inaccessible. **All access to this account will be lost.** #### createPure ```dart createPure({ required _i11.ProxyType proxyType, required int delay, required int index, ``` Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and initialize it with a proxy of `proxy_type` for `origin` sender. Requires a `Signed` origin. - `proxy_type`: The type of the proxy that the sender will be registered as over the new account. This will almost always be the most permissive `ProxyType` possible to allow for maximum flexibility. - `index`: A disambiguation index, in case this is called multiple times in the same transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just want to use `0`. - `delay`: The announcement period required of the initial proxy. Will generally be zero. Fails with `Duplicate` if this has already been called in this transaction, from the same sender, with the same parameters. Fails if there are insufficient funds to pay for deposit. #### killPure ```dart killPure({ required _i10.MultiAddress spawner, required _i11.ProxyType proxyType, required int index, required BigInt height, required BigInt extIndex, ``` Removes a previously spawned pure proxy. WARNING: **All access to this account will be lost.** Any funds held in it will be inaccessible. Requires a `Signed` origin, and the sender account must have been created by a call to `pure` with corresponding parameters. - `spawner`: The account that originally called `pure` to create this account. - `index`: The disambiguation index originally passed to `pure`. Probably `0`. - `proxy_type`: The proxy type originally passed to `pure`. - `height`: The height of the chain when the call to `pure` was processed. - `ext_index`: The extrinsic index in which the call to `pure` was processed. Fails with `NoPermission` in case the caller is not a previously created pure account whose `pure` call has corresponding parameters. #### announce ```dart announce({ required _i10.MultiAddress real, required _i13.H256 callHash, ``` Publish the hash of a proxy-call that will be made in the future. This must be called some number of blocks before the corresponding `proxy` is attempted if the delay associated with the proxy relationship is greater than zero. No more than `MaxPending` announcements may be made at any one time. This will take a deposit of `AnnouncementDepositFactor` as well as `AnnouncementDepositBase` if there are no other pending announcements. The dispatch origin for this call must be *Signed* and a proxy of `real`. Parameters: - `real`: The account that the proxy will make a call on behalf of. - `call_hash`: The hash of the call to be made by the `real` account. #### removeAnnouncement ```dart removeAnnouncement({ required _i10.MultiAddress real, required _i13.H256 callHash, ``` Remove a given announcement. May be called by a proxy account to remove a call they previously announced and return the deposit. The dispatch origin for this call must be *Signed*. Parameters: - `real`: The account that the proxy will make a call on behalf of. - `call_hash`: The hash of the call to be made by the `real` account. #### rejectAnnouncement ```dart rejectAnnouncement({ required _i10.MultiAddress delegate, required _i13.H256 callHash, ``` Remove the given announcement of a delegate. May be called by a target (proxied) account to remove a call that one of their delegates (`delegate`) has announced they want to execute. The deposit is returned. The dispatch origin for this call must be *Signed*. Parameters: - `delegate`: The account that previously announced the call. - `call_hash`: The hash of the call to be made. #### proxyAnnounced ```dart proxyAnnounced({ required _i10.MultiAddress delegate, required _i10.MultiAddress real, _i11.ProxyType? forceProxyType, required _i9.RuntimeCall call, ``` Dispatch the given `call` from an account that the sender is authorized for through `add_proxy`. Removes any corresponding announcement(s). The dispatch origin for this call must be *Signed*. Parameters: - `real`: The account that the proxy will make a call on behalf of. - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call. - `call`: The call to be made by the `real` account. ### multisig #### asMultiThreshold1 ```dart asMultiThreshold1({ required List<_i2.AccountId32> otherSignatories, required _i7.RuntimeCall call, ``` Immediately dispatch a multi-signature call using a single approval from the caller. The dispatch origin for this call must be *Signed*. - `other_signatories`: The accounts (other than the sender) who are part of the multi-signature, but do not participate in the approval process. - `call`: The call to be executed. Result is equivalent to the dispatched result. - Complexity O(Z + C) where Z is the length of the call and C its execution weight. #### asMulti ```dart asMulti({ required int threshold, required List<_i2.AccountId32> otherSignatories, _i9.Timepoint? maybeTimepoint, required _i7.RuntimeCall call, required _i10.Weight maxWeight, ) { ``` Register approval for a dispatch to be made from a deterministic composite account if approved by a total of `threshold - 1` of `other_signatories`. If there are enough, then dispatch the call. Payment: `DepositBase` will be reserved if this is the first approval, plus `threshold` times `DepositFactor`. It is returned once this dispatch happens or is cancelled. The dispatch origin for this call must be *Signed*. - `threshold`: The total number of approvals for this dispatch before it is executed. - `other_signatories`: The accounts (other than the sender) who can approve this dispatch. May not be empty. - `maybe_timepoint`: If this is the first approval, then this must be `None`. If it is not the first approval, then it must be `Some`, with the timepoint (block number and transaction index) of the first approval transaction. - `call`: The call to be executed. NOTE: Unless this is the final approval, you will generally want to use `approve_as_multi` instead, since it only requires a hash of the call. Result is equivalent to the dispatched result if `threshold` is exactly `1`. Otherwise on success, result is `Ok` and the result from the interior call, if it was executed, may be found in the deposited `MultisigExecuted` event. - Complexity - `O(S + Z + Call)`. - Up to one balance-reserve or unreserve operation. - One passthrough operation, one insert, both `O(S)` where `S` is the number of signatories. `S` is capped by `MaxSignatories`, with weight being proportional. - One call encode & hash, both of complexity `O(Z)` where `Z` is tx-len. - One encode & hash, both of complexity `O(S)`. - Up to one binary search and insert (`O(logS + S)`). - I/O: 1 read `O(S)`, up to 1 mutate `O(S)`. Up to one remove. - One event. - The weight of the `call`. - Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit taken for its lifetime of `DepositBase + threshold * DepositFactor`. #### approveAsMulti ```dart approveAsMulti({ required int threshold, required List<_i2.AccountId32> otherSignatories, _i9.Timepoint? maybeTimepoint, required List callHash, required _i10.Weight maxWeight, ) { ``` Register approval for a dispatch to be made from a deterministic composite account if approved by a total of `threshold - 1` of `other_signatories`. Payment: `DepositBase` will be reserved if this is the first approval, plus `threshold` times `DepositFactor`. It is returned once this dispatch happens or is cancelled. The dispatch origin for this call must be *Signed*. - `threshold`: The total number of approvals for this dispatch before it is executed. - `other_signatories`: The accounts (other than the sender) who can approve this dispatch. May not be empty. - `maybe_timepoint`: If this is the first approval, then this must be `None`. If it is not the first approval, then it must be `Some`, with the timepoint (block number and transaction index) of the first approval transaction. - `call_hash`: The hash of the call to be executed. NOTE: If this is the final approval, you will want to use `as_multi` instead. - Complexity - `O(S)`. - Up to one balance-reserve or unreserve operation. - One passthrough operation, one insert, both `O(S)` where `S` is the number of signatories. `S` is capped by `MaxSignatories`, with weight being proportional. - One encode & hash, both of complexity `O(S)`. - Up to one binary search and insert (`O(logS + S)`). - I/O: 1 read `O(S)`, up to 1 mutate `O(S)`. Up to one remove. - One event. - Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit taken for its lifetime of `DepositBase + threshold * DepositFactor`. #### cancelAsMulti ```dart cancelAsMulti({ required int threshold, required List<_i2.AccountId32> otherSignatories, required _i9.Timepoint timepoint, required List callHash, ``` Cancel a pre-existing, on-going multisig transaction. Any deposit reserved previously for this operation will be unreserved on success. The dispatch origin for this call must be *Signed*. - `threshold`: The total number of approvals for this dispatch before it is executed. - `other_signatories`: The accounts (other than the sender) who can approve this dispatch. May not be empty. - `timepoint`: The timepoint (block number and transaction index) of the first approval transaction for this dispatch. - `call_hash`: The hash of the call to be executed. - Complexity - `O(S)`. - Up to one balance-reserve or unreserve operation. - One passthrough operation, one insert, both `O(S)` where `S` is the number of signatories. `S` is capped by `MaxSignatories`, with weight being proportional. - One encode & hash, both of complexity `O(S)`. - One event. - I/O: 1 read `O(S)`, one remove. - Storage: removes one item. ### bounties #### proposeBounty ```dart proposeBounty({ required BigInt value, required List description, ``` Propose a new bounty. The dispatch origin for this call must be *Signed*. Payment: `TipReportDepositBase` will be reserved from the origin account, as well as `DataDepositPerByte` for each byte in `reason`. It will be unreserved upon approval, or slashed when rejected. - `curator`: The curator account whom will manage this bounty. - `fee`: The curator fee. - `value`: The total payment amount of this bounty, curator fee included. - `description`: The description of this bounty. #### approveBounty ```dart approveBounty({required BigInt bountyId}) { ``` Approve a bounty proposal. At a later time, the bounty will be funded and become active and the original deposit will be returned. May only be called from `T::SpendOrigin`. - Complexity - O(1). #### proposeCurator ```dart proposeCurator({ required BigInt bountyId, required _i8.MultiAddress curator, required BigInt fee, ``` Propose a curator to a funded bounty. May only be called from `T::SpendOrigin`. - Complexity - O(1). #### unassignCurator ```dart unassignCurator({required BigInt bountyId}) { ``` Unassign curator from a bounty. This function can only be called by the `RejectOrigin` a signed origin. If this function is called by the `RejectOrigin`, we assume that the curator is malicious or inactive. As a result, we will slash the curator when possible. If the origin is the curator, we take this as a sign they are unable to do their job and they willingly give up. We could slash them, but for now we allow them to recover their deposit and exit without issue. (We may want to change this if it is abused.) Finally, the origin can be anyone if and only if the curator is "inactive". This allows anyone in the community to call out that a curator is not doing their due diligence, and we should pick a new curator. In this case the curator should also be slashed. - Complexity - O(1). #### acceptCurator ```dart acceptCurator({required BigInt bountyId}) { ``` Accept the curator role for a bounty. A deposit will be reserved from curator and refund upon successful payout. May only be called from the curator. - Complexity - O(1). #### awardBounty ```dart awardBounty({ required BigInt bountyId, required _i8.MultiAddress beneficiary, ``` Award bounty to a beneficiary account. The beneficiary will be able to claim the funds after a delay. The dispatch origin for this call must be the curator of this bounty. - `bounty_id`: Bounty ID to award. - `beneficiary`: The beneficiary account whom will receive the payout. - Complexity - O(1). #### claimBounty ```dart claimBounty({required BigInt bountyId}) { ``` Claim the payout from an awarded bounty after payout delay. The dispatch origin for this call must be the beneficiary of this bounty. - `bounty_id`: Bounty ID to claim. - Complexity - O(1). #### closeBounty ```dart closeBounty({required BigInt bountyId}) { ``` Cancel a proposed or active bounty. All the funds will be sent to treasury and the curator deposit will be unreserved if possible. Only `T::RejectOrigin` is able to cancel a bounty. - `bounty_id`: Bounty ID to cancel. - Complexity - O(1). #### extendBountyExpiry ```dart extendBountyExpiry({ required BigInt bountyId, required List remark, ``` Extend the expiry time of an active bounty. The dispatch origin for this call must be the curator of this bounty. - `bounty_id`: Bounty ID to extend. - `remark`: additional information. - Complexity - O(1). ### childBounties #### addChildBounty ```dart addChildBounty({ required BigInt parentBountyId, required BigInt value, required List description, ``` Add a new child-bounty. The dispatch origin for this call must be the curator of parent bounty and the parent bounty must be in "active" state. Child-bounty gets added successfully & fund gets transferred from parent bounty to child-bounty account, if parent bounty has enough funds, else the call fails. Upper bound to maximum number of active child bounties that can be added are managed via runtime trait config [`Config::MaxActiveChildBountyCount`]. If the call is success, the status of child-bounty is updated to "Added". - `parent_bounty_id`: Index of parent bounty for which child-bounty is being added. - `value`: Value for executing the proposal. - `description`: Text description for the child-bounty. #### proposeCurator ```dart proposeCurator({ required BigInt parentBountyId, required BigInt childBountyId, required _i8.MultiAddress curator, required BigInt fee, ``` Propose curator for funded child-bounty. The dispatch origin for this call must be curator of parent bounty. Parent bounty must be in active state, for this child-bounty call to work. Child-bounty must be in "Added" state, for processing the call. And state of child-bounty is moved to "CuratorProposed" on successful call completion. - `parent_bounty_id`: Index of parent bounty. - `child_bounty_id`: Index of child bounty. - `curator`: Address of child-bounty curator. - `fee`: payment fee to child-bounty curator for execution. #### acceptCurator ```dart acceptCurator({ required BigInt parentBountyId, required BigInt childBountyId, ``` Accept the curator role for the child-bounty. The dispatch origin for this call must be the curator of this child-bounty. A deposit will be reserved from the curator and refund upon successful payout or cancellation. Fee for curator is deducted from curator fee of parent bounty. Parent bounty must be in active state, for this child-bounty call to work. Child-bounty must be in "CuratorProposed" state, for processing the call. And state of child-bounty is moved to "Active" on successful call completion. - `parent_bounty_id`: Index of parent bounty. - `child_bounty_id`: Index of child bounty. #### unassignCurator ```dart unassignCurator({ required BigInt parentBountyId, required BigInt childBountyId, ``` Unassign curator from a child-bounty. The dispatch origin for this call can be either `RejectOrigin`, or the curator of the parent bounty, or any signed origin. For the origin other than T::RejectOrigin and the child-bounty curator, parent bounty must be in active state, for this call to work. We allow child-bounty curator and T::RejectOrigin to execute this call irrespective of the parent bounty state. If this function is called by the `RejectOrigin` or the parent bounty curator, we assume that the child-bounty curator is malicious or inactive. As a result, child-bounty curator deposit is slashed. If the origin is the child-bounty curator, we take this as a sign that they are unable to do their job, and are willingly giving up. We could slash the deposit, but for now we allow them to unreserve their deposit and exit without issue. (We may want to change this if it is abused.) Finally, the origin can be anyone iff the child-bounty curator is "inactive". Expiry update due of parent bounty is used to estimate inactive state of child-bounty curator. This allows anyone in the community to call out that a child-bounty curator is not doing their due diligence, and we should pick a new one. In this case the child-bounty curator deposit is slashed. State of child-bounty is moved to Added state on successful call completion. - `parent_bounty_id`: Index of parent bounty. - `child_bounty_id`: Index of child bounty. #### awardChildBounty ```dart awardChildBounty({ required BigInt parentBountyId, required BigInt childBountyId, required _i8.MultiAddress beneficiary, ``` Award child-bounty to a beneficiary. The beneficiary will be able to claim the funds after a delay. The dispatch origin for this call must be the parent curator or curator of this child-bounty. Parent bounty must be in active state, for this child-bounty call to work. Child-bounty must be in active state, for processing the call. And state of child-bounty is moved to "PendingPayout" on successful call completion. - `parent_bounty_id`: Index of parent bounty. - `child_bounty_id`: Index of child bounty. - `beneficiary`: Beneficiary account. #### claimChildBounty ```dart claimChildBounty({ required BigInt parentBountyId, required BigInt childBountyId, ``` Claim the payout from an awarded child-bounty after payout delay. The dispatch origin for this call may be any signed origin. Call works independent of parent bounty state, No need for parent bounty to be in active state. The Beneficiary is paid out with agreed bounty value. Curator fee is paid & curator deposit is unreserved. Child-bounty must be in "PendingPayout" state, for processing the call. And instance of child-bounty is removed from the state on successful call completion. - `parent_bounty_id`: Index of parent bounty. - `child_bounty_id`: Index of child bounty. #### closeChildBounty ```dart closeChildBounty({ required BigInt parentBountyId, required BigInt childBountyId, ``` Cancel a proposed or active child-bounty. Child-bounty account funds are transferred to parent bounty account. The child-bounty curator deposit may be unreserved if possible. The dispatch origin for this call must be either parent curator or `T::RejectOrigin`. If the state of child-bounty is `Active`, curator deposit is unreserved. If the state of child-bounty is `PendingPayout`, call fails & returns `PendingPayout` error. For the origin other than T::RejectOrigin, parent bounty must be in active state, for this child-bounty call to work. For origin T::RejectOrigin execution is forced. Instance of child-bounty is removed from the state on successful call completion. - `parent_bounty_id`: Index of parent bounty. - `child_bounty_id`: Index of child bounty. ### electionProviderMultiPhase #### submitUnsigned ```dart submitUnsigned({ required _i13.RawSolution rawSolution, required _i6.SolutionOrSnapshotSize witness, ``` Submit a solution for the unsigned phase. The dispatch origin fo this call must be **none**. This submission is checked on the fly. Moreover, this unsigned solution is only validated when submitted to the pool from the **local** node. Effectively, this means that only active validators can submit this transaction when authoring a block (similar to an inherent). To prevent any incorrect solution (and thus wasted time/weight), this transaction will panic if the solution submitted by the validator is invalid in any way, effectively putting their authoring reward at risk. No deposit or reward is associated with this submission. #### setMinimumUntrustedScore ```dart setMinimumUntrustedScore( {_i8.ElectionScore? maybeNextScore}) { ``` Set a new value for `MinimumUntrustedScore`. Dispatch origin must be aligned with `T::ForceOrigin`. This check can be turned off by setting the value to `None`. #### setEmergencyElectionResult ```dart setEmergencyElectionResult( {required List<_i7.Tuple2<_i15.AccountId32, _i16.Support>> supports}) { ``` Set a solution in the queue, to be handed out to the client of this pallet in the next call to `ElectionProvider::elect`. This can only be set by `T::ForceOrigin`, and only when the phase is `Emergency`. The solution is not checked for any feasibility and is assumed to be trustworthy, as any feasibility check itself can in principle cause the election process to fail (due to memory/weight constrains). #### submit ```dart submit({required _i13.RawSolution rawSolution}) { ``` Submit a solution for the signed phase. The dispatch origin fo this call must be **signed**. The solution is potentially queued, based on the claimed score and processed at the end of the signed phase. A deposit is reserved and recorded for the solution. Based on the outcome, the solution might be rewarded, slashed, or get all or a part of the deposit back. #### governanceFallback ```dart governanceFallback({ int? maybeMaxVoters, int? maybeMaxTargets, ``` Trigger the governance fallback. This can only be called when [`Phase::Emergency`] is enabled, as an alternative to calling [`Call::set_emergency_election_result`]. ### voterList #### rebag ```dart rebag({required _i9.MultiAddress dislocated}) { ``` Declare that some `dislocated` account has, through rewards or penalties, sufficiently changed its score that it should properly fall into a different bag than its current one. Anyone can call this function about any potentially dislocated account. Will always update the stored score of `dislocated` to the correct score, based on `ScoreProvider`. If `dislocated` does not exists, it returns an error. #### putInFrontOf ```dart putInFrontOf({required _i9.MultiAddress lighter}) { ``` Move the caller's Id directly in front of `lighter`. The dispatch origin for this call must be *Signed* and can only be called by the Id of the account going in front of `lighter`. Fee is payed by the origin under all circumstances. Only works if: - both nodes are within the same bag, - and `origin` has a greater `Score` than `lighter`. #### putInFrontOfOther ```dart putInFrontOfOther({ required _i9.MultiAddress heavier, required _i9.MultiAddress lighter, ``` Same as [`Pallet::put_in_front_of`], but it can be called by anyone. Fee is paid by the origin under all circumstances. ### nominationPools #### join ```dart join({ required BigInt amount, required int poolId, ``` Stake funds with a pool. The amount to bond is transferred from the member to the pools account and immediately increases the pools bond. # Note - An account can only be a member of a single pool. - An account cannot join the same pool multiple times. - This call will *not* dust the member account, so the member must have at least `existential deposit + amount` in their account. - Only a pool with [`PoolState::Open`] can be joined #### bondExtra ```dart bondExtra({required _i14.BondExtra extra}) { ``` Bond `extra` more funds from `origin` into the pool to which they already belong. Additional funds can come from either the free balance of the account, of from the accumulated rewards, see [`BondExtra`]. Bonding extra funds implies an automatic payout of all pending rewards as well. See `bond_extra_other` to bond pending rewards of `other` members. #### claimPayout ```dart claimPayout() { ``` A bonded member can use this to claim their payout based on the rewards that the pool has accumulated since their last claimed payout (OR since joining if this is their first time claiming rewards). The payout will be transferred to the member's account. The member will earn rewards pro rata based on the members stake vs the sum of the members in the pools stake. Rewards do not "expire". See `claim_payout_other` to claim rewards on behalf of some `other` pool member. #### unbond ```dart unbond({ required _i15.MultiAddress memberAccount, required BigInt unbondingPoints, ``` Unbond up to `unbonding_points` of the `member_account`'s funds from the pool. It implicitly collects the rewards one last time, since not doing so would mean some rewards would be forfeited. Under certain conditions, this call can be dispatched permissionlessly (i.e. by any account). - Conditions for a permissionless dispatch. * The pool is blocked and the caller is either the root or bouncer. This is refereed to as a kick. * The pool is destroying and the member is not the depositor. * The pool is destroying, the member is the depositor and no other members are in the pool. - Conditions for permissioned dispatch (i.e. the caller is also the `member_account`): * The caller is not the depositor. * The caller is the depositor, the pool is destroying and no other members are in the pool. # Note If there are too many unlocking chunks to unbond with the pool account, [`Call::pool_withdraw_unbonded`] can be called to try and minimize unlocking chunks. The [`StakingInterface::unbond`] will implicitly call [`Call::pool_withdraw_unbonded`] to try to free chunks if necessary (ie. if unbound was called and no unlocking chunks are available). However, it may not be possible to release the current unlocking chunks, in which case, the result of this call will likely be the `NoMoreChunks` error from the staking system. #### poolWithdrawUnbonded ```dart poolWithdrawUnbonded({ required int poolId, required int numSlashingSpans, ``` Call `withdraw_unbonded` for the pools account. This call can be made by any account. This is useful if there are too many unlocking chunks to call `unbond`, and some can be cleared by withdrawing. In the case there are too many unlocking chunks, the user would probably see an error like `NoMoreChunks` emitted from the staking system when they attempt to unbond. #### withdrawUnbonded ```dart withdrawUnbonded({ required _i15.MultiAddress memberAccount, required int numSlashingSpans, ``` Withdraw unbonded funds from `member_account`. If no bonded funds can be unbonded, an error is returned. Under certain conditions, this call can be dispatched permissionlessly (i.e. by any account). # Conditions for a permissionless dispatch - The pool is in destroy mode and the target is not the depositor. - The target is the depositor and they are the only member in the sub pools. - The pool is blocked and the caller is either the root or bouncer. # Conditions for permissioned dispatch - The caller is the target and they are not the depositor. # Note - If the target is the depositor, the pool will be destroyed. - If the pool has any pending slash, we also try to slash the member before letting them withdraw. This calculation adds some weight overhead and is only defensive. In reality, pool slashes must have been already applied via permissionless [`Call::apply_slash`]. #### create ```dart create({ required BigInt amount, required _i15.MultiAddress root, required _i15.MultiAddress nominator, required _i15.MultiAddress bouncer, ``` Create a new delegation pool. # Arguments - `amount` - The amount of funds to delegate to the pool. This also acts of a sort of deposit since the pools creator cannot fully unbond funds until the pool is being destroyed. - `index` - A disambiguation index for creating the account. Likely only useful when creating multiple pools in the same extrinsic. - `root` - The account to set as [`PoolRoles::root`]. - `nominator` - The account to set as the [`PoolRoles::nominator`]. - `bouncer` - The account to set as the [`PoolRoles::bouncer`]. # Note In addition to `amount`, the caller will transfer the existential deposit; so the caller needs at have at least `amount + existential_deposit` transferable. #### createWithPoolId ```dart createWithPoolId({ required BigInt amount, required _i15.MultiAddress root, required _i15.MultiAddress nominator, required _i15.MultiAddress bouncer, required int poolId, ``` Create a new delegation pool with a previously used pool id # Arguments same as `create` with the inclusion of - `pool_id` - \`A valid PoolId. #### nominate ```dart nominate({ required int poolId, required List<_i4.AccountId32> validators, ``` Nominate on behalf of the pool. The dispatch origin of this call must be signed by the pool nominator or the pool root role. This directly forward the call to the staking pallet, on behalf of the pool bonded account. # Note In addition to a `root` or `nominator` role of `origin`, pool's depositor needs to have at least `depositor_min_bond` in the pool to start nominating. #### setState ```dart setState({ required int poolId, required _i16.PoolState state, ``` Set a new state for the pool. If a pool is already in the `Destroying` state, then under no condition can its state change again. The dispatch origin of this call must be either: 1. signed by the bouncer, or the root role of the pool, 2. if the pool conditions to be open are NOT met (as described by `ok_to_be_open`), and then the state of the pool can be permissionlessly changed to `Destroying`. #### setMetadata ```dart setMetadata({ required int poolId, required List metadata, ``` Set a new metadata for the pool. The dispatch origin of this call must be signed by the bouncer, or the root role of the pool. #### setConfigs ```dart setConfigs({ required _i17.ConfigOp minJoinBond, required _i17.ConfigOp minCreateBond, required _i18.ConfigOp maxPools, required _i18.ConfigOp maxMembers, required _i18.ConfigOp maxMembersPerPool, required _i19.ConfigOp globalMaxCommission, ``` Update configurations for the nomination pools. The origin for this call must be [`Config::AdminOrigin`]. # Arguments - `min_join_bond` - Set [`MinJoinBond`]. - `min_create_bond` - Set [`MinCreateBond`]. - `max_pools` - Set [`MaxPools`]. - `max_members` - Set [`MaxPoolMembers`]. - `max_members_per_pool` - Set [`MaxPoolMembersPerPool`]. - `global_max_commission` - Set [`GlobalMaxCommission`]. #### updateRoles ```dart updateRoles({ required int poolId, required _i20.ConfigOp newRoot, required _i20.ConfigOp newNominator, required _i20.ConfigOp newBouncer, ``` Update the roles of the pool. The root is the only entity that can change any of the roles, including itself, excluding the depositor, who can never change. It emits an event, notifying UIs of the role change. This event is quite relevant to most pool members and they should be informed of changes to pool roles. #### chill ```dart chill({required int poolId}) { ``` Chill on behalf of the pool. The dispatch origin of this call can be signed by the pool nominator or the pool root role, same as [`Pallet::nominate`]. Under certain conditions, this call can be dispatched permissionlessly (i.e. by any account). # Conditions for a permissionless dispatch: - When pool depositor has less than `MinNominatorBond` staked, otherwise pool members are unable to unbond. # Conditions for permissioned dispatch: - The caller has a nominator or root role of the pool. This directly forward the call to the staking pallet, on behalf of the pool bonded account. #### bondExtraOther ```dart bondExtraOther({ required _i15.MultiAddress member, required _i14.BondExtra extra, ``` `origin` bonds funds from `extra` for some pool member `member` into their respective pools. `origin` can bond extra funds from free balance or pending rewards when `origin == other`. In the case of `origin != other`, `origin` can only bond extra pending rewards of `other` members assuming set\_claim\_permission for the given member is `PermissionlessCompound` or `PermissionlessAll`. #### setClaimPermission ```dart setClaimPermission( {required _i9.ClaimPermission permission}) { ``` Allows a pool member to set a claim permission to allow or disallow permissionless bonding and withdrawing. # Arguments - `origin` - Member of a pool. - `permission` - The permission to be applied. #### claimPayoutOther ```dart claimPayoutOther({required _i4.AccountId32 other}) { ``` `origin` can claim payouts on some pool member `other`'s behalf. Pool member `other` must have a `PermissionlessWithdraw` or `PermissionlessAll` claim permission for this call to be successful. #### setCommission ```dart setCommission({ required int poolId, _i21.Tuple2<_i3.Perbill, _i4.AccountId32>? newCommission, ``` Set the commission of a pool. Both a commission percentage and a commission payee must be provided in the `current` tuple. Where a `current` of `None` is provided, any current commission will be removed. - If a `None` is supplied to `new_commission`, existing commission will be removed. #### setCommissionMax ```dart setCommissionMax({ required int poolId, required _i3.Perbill maxCommission, ``` Set the maximum commission of a pool. - Initial max can be set to any `Perbill`, and only smaller values thereafter. - Current commission will be lowered in the event it is higher than a new max commission. #### setCommissionChangeRate ```dart setCommissionChangeRate({ required int poolId, required _i22.CommissionChangeRate changeRate, ``` Set the commission change rate for a pool. Initial change rate is not bounded, whereas subsequent updates can only be more restrictive than the current. #### claimCommission ```dart claimCommission({required int poolId}) { ``` Claim pending commission. The dispatch origin of this call must be signed by the `root` role of the pool. Pending commission is paid out and added to total claimed commission\`. Total pending commission is reset to zero. the current. #### adjustPoolDeposit ```dart adjustPoolDeposit({required int poolId}) { ``` Top up the deficit or withdraw the excess ED from the pool. When a pool is created, the pool depositor transfers ED to the reward account of the pool. ED is subject to change and over time, the deposit in the reward account may be insufficient to cover the ED deficit of the pool or vice-versa where there is excess deposit to the pool. This call allows anyone to adjust the ED deposit of the pool by either topping up the deficit or claiming the excess. #### setCommissionClaimPermission ```dart setCommissionClaimPermission({ required int poolId, _i23.CommissionClaimPermission? permission, ``` Set or remove a pool's commission claim permission. Determines who can claim the pool's pending commission. Only the `Root` role of the pool is able to configure commission claim permissions. #### applyStash ```dart applySlash({required _i15.MultiAddress memberAccount}) { ``` Apply a pending slash on a member. Fails unless [`crate::pallet::Config::StakeAdapter`] is of strategy type: [`adapter::StakeStrategyType::Delegate`]. This call can be dispatched permissionlessly (i.e. by any account). If the member has slash to be applied, caller may be rewarded with the part of the slash. #### migrateDelegation ```dart migrateDelegation( {required _i15.MultiAddress memberAccount}) { ``` Migrates delegated funds from the pool account to the `member_account`. Fails unless [`crate::pallet::Config::StakeAdapter`] is of strategy type: [`adapter::StakeStrategyType::Delegate`]. This is a permission-less call and refunds any fee if claim is successful. If the pool has migrated to delegation based staking, the staked tokens of pool members can be moved and held in their own account. See [`adapter::DelegateStake`] #### migratePoolToDelegateStake ```dart migratePoolToDelegateStake({required int poolId}) { ``` Migrate pool from [`adapter::StakeStrategyType::Transfer`] to [`adapter::StakeStrategyType::Delegate`]. Fails unless [`crate::pallet::Config::StakeAdapter`] is of strategy type: [`adapter::StakeStrategyType::Delegate`]. This call can be dispatched permissionlessly, and refunds any fee if successful. If the pool has already migrated to delegation based staking, this call will fail. ### fastUnstake #### registerFastUnstake ```dart registerFastUnstake() { ``` Register oneself for fast-unstake. - Dispatch Origin The dispatch origin of this call must be *signed* by whoever is permitted to call unbond funds by the staking system. See [`Config::Staking`]. - Details The stash associated with the origin must have no ongoing unlocking chunks. If successful, this will fully unbond and chill the stash. Then, it will enqueue the stash to be checked in further blocks. If by the time this is called, the stash is actually eligible for fast-unstake, then they are guaranteed to remain eligible, because the call will chill them as well. If the check works, the entire staking data is removed, i.e. the stash is fully unstaked. If the check fails, the stash remains chilled and waiting for being unbonded as in with the normal staking system, but they lose part of their unbonding chunks due to consuming the chain's resources. - Events Some events from the staking and currency system might be emitted. #### deregister ```dart deregister() { ``` Deregister oneself from the fast-unstake. - Dispatch Origin The dispatch origin of this call must be *signed* by whoever is permitted to call unbond funds by the staking system. See [`Config::Staking`]. - Details This is useful if one is registered, they are still waiting, and they change their mind. Note that the associated stash is still fully unbonded and chilled as a consequence of calling [`Pallet::register_fast_unstake`]. Therefore, this should probably be followed by a call to `rebond` in the staking system. - Events Some events from the staking and currency system might be emitted. #### control ```dart control({required int erasToCheck}) { ``` Control the operation of this pallet. - Dispatch Origin The dispatch origin of this call must be [`Config::ControlOrigin`]. - Details Can set the number of eras to check per block, and potentially other admin work. - Events No events are emitted from this dispatch. ### configuration #### setValidationUpgradeCooldown ```dart setValidationUpgradeCooldown({required int new_}) { ``` Set the validation upgrade cooldown. #### setValidationUpgradeDelay ```dart setValidationUpgradeDelay({required int new_}) { ``` Set the validation upgrade delay. #### setCodeRetentionPeriod ```dart setCodeRetentionPeriod({required int new_}) { ``` Set the acceptance period for an included candidate. #### setMaxCodeSize ```dart setMaxCodeSize({required int new_}) { ``` Set the max validation code size for incoming upgrades. #### setMaxPovSize ```dart setMaxPovSize({required int new_}) { ``` Set the max POV block size for incoming upgrades. #### setMaxHeadDataSize ```dart setMaxHeadDataSize({required int new_}) { ``` Set the max head data size for paras. #### setCoretimeCores ```dart setCoretimeCores({required int new_}) { ``` Set the number of coretime execution cores. NOTE: that this configuration is managed by the coretime chain. Only manually change this, if you really know what you are doing! #### setMaxAvailabilityTimeouts ```dart setMaxAvailabilityTimeouts({required int new_}) { ``` Set the max number of times a claim may timeout on a core before it is abandoned #### setGroupRotationFrequency ```dart setGroupRotationFrequency({required int new_}) { ``` Set the parachain validator-group rotation frequency #### setParasAvailabilityPeriod ```dart setParasAvailabilityPeriod({required int new_}) { ``` Set the availability period for paras. #### setSchedulingLookahead ```dart setSchedulingLookahead({required int new_}) { ``` Set the scheduling lookahead, in expected number of blocks at peak throughput. #### setMaxValidatorsPerCore ```dart setMaxValidatorsPerCore({int? new_}) { ``` Set the maximum number of validators to assign to any core. #### setMaxValidators ```dart setMaxValidators({int? new_}) { ``` Set the maximum number of validators to use in parachain consensus. #### setDisputePeriod ```dart setDisputePeriod({required int new_}) { ``` Set the dispute period, in number of sessions to keep for disputes. #### setDisputePostConclusionAcceptancePeriod ```dart setDisputePostConclusionAcceptancePeriod( {required int new_}) { ``` Set the dispute post conclusion acceptance period. #### setNoShowSlots ```dart setNoShowSlots({required int new_}) { ``` Set the no show slots, in number of number of consensus slots. Must be at least 1. #### setNDelayTranches ```dart setNDelayTranches({required int new_}) { ``` Set the total number of delay tranches. #### setZerothDelayTranceWidth ```dart setZerothDelayTrancheWidth({required int new_}) { ``` Set the zeroth delay tranche width. #### setNeededApprovals ```dart setNeededApprovals({required int new_}) { ``` Set the number of validators needed to approve a block. #### setRelayVrfModuloSamples ```dart setRelayVrfModuloSamples({required int new_}) { ``` Set the number of samples to do of the `RelayVRFModulo` approval assignment criterion. #### setMaxUpwardQueueCount ```dart setMaxUpwardQueueCount({required int new_}) { ``` Sets the maximum items that can present in a upward dispatch queue at once. #### setMaxUpwardQueueSize ```dart setMaxUpwardQueueSize({required int new_}) { ``` Sets the maximum total size of items that can present in a upward dispatch queue at once. #### setMaxDownwardMessageSize ```dart setMaxDownwardMessageSize({required int new_}) { ``` Set the critical downward message size. #### setMaxUpwardMessageSize ```dart setMaxUpwardMessageSize({required int new_}) { ``` Sets the maximum size of an upward message that can be sent by a candidate. #### setMaxUpwardMessageNumPerCandidate ```dart setMaxUpwardMessageNumPerCandidate({required int new_}) { ``` Sets the maximum number of messages that a candidate can contain. #### setHrmpOpenRequestTtl ```dart setHrmpOpenRequestTtl({required int new_}) { ``` Sets the number of sessions after which an HRMP open channel request expires. #### setHrmpSenderDeposit ```dart setHrmpSenderDeposit({required BigInt new_}) { ``` Sets the amount of funds that the sender should provide for opening an HRMP channel. #### setHrmpRecipientDeposit ```dart setHrmpRecipientDeposit({required BigInt new_}) { ``` Sets the amount of funds that the recipient should provide for accepting opening an HRMP channel. #### setHrmpChannelMaxCapacity ```dart setHrmpChannelMaxCapacity({required int new_}) { ``` Sets the maximum number of messages allowed in an HRMP channel at once. #### setHrmpChannelMaxTotalSize ```dart setHrmpChannelMaxTotalSize({required int new_}) { ``` Sets the maximum total size of messages in bytes allowed in an HRMP channel at once. #### setHrmpMaxParachainInboundChannels ```dart setHrmpMaxParachainInboundChannels({required int new_}) { ``` Sets the maximum number of inbound HRMP channels a parachain is allowed to accept. #### setHrmpChannelMaxMessageSize ```dart setHrmpChannelMaxMessageSize({required int new_}) { ``` Sets the maximum size of a message that could ever be put into an HRMP channel. #### setHrmpMaxParachainOutboundChannels ```dart setHrmpMaxParachainOutboundChannels({required int new_}) { ``` Sets the maximum number of outbound HRMP channels a parachain is allowed to open. #### setHrmpMaxMessageNumPerCandidate ```dart setHrmpMaxMessageNumPerCandidate({required int new_}) { ``` Sets the maximum number of outbound HRMP messages can be sent by a candidate. #### setPvfVotingTtl ```dart setPvfVotingTtl({required int new_}) { ``` Set the number of session changes after which a PVF pre-checking voting is rejected. #### setMinimumValidationUpgradeDelay ```dart setMinimumValidationUpgradeDelay({required int new_}) { ``` Sets the minimum delay between announcing the upgrade block for a parachain until the upgrade taking place. See the field documentation for information and constraints for the new value. #### setBypassConsistencyCheck ```dart setBypassConsistencyCheck({required bool new_}) { ``` Setting this to true will disable consistency checks for the configuration setters. Use with caution. #### setAsyncBackingParams ```dart setAsyncBackingParams( {required _i6.AsyncBackingParams new_}) { ``` Set the asynchronous backing parameters. #### setExecutorParams ```dart setExecutorParams({required _i12.ExecutorParams new_}) { ``` Set PVF executor parameters. #### setOnDemandBaseFee ```dart setOnDemandBaseFee({required BigInt new_}) { ``` Set the on demand (parathreads) base fee. #### setOnDemandFeeVariability ```dart setOnDemandFeeVariability({required _i13.Perbill new_}) { ``` Set the on demand (parathreads) fee variability. #### setOnDemandQueueMaxSize ```dart setOnDemandQueueMaxSize({required int new_}) { ``` Set the on demand (parathreads) queue max size. #### setOnDemandTargetQueueUtilization ```dart setOnDemandTargetQueueUtilization( {required _i13.Perbill new_}) { ``` Set the on demand (parathreads) fee variability. #### setOnDemandTtl ```dart setOnDemandTtl({required int new_}) { ``` Set the on demand (parathreads) ttl in the claimqueue. #### setMinimumBackingVotes ```dart setMinimumBackingVotes({required int new_}) { ``` Set the minimum backing votes threshold. #### setNodeFeature ```dart setNodeFeature({ required int index, required bool value, ``` Set/Unset a node feature. #### setApprovalVotingParams ```dart setApprovalVotingParams( {required _i8.ApprovalVotingParams new_}) { ``` Set approval-voting-params. #### setSchedulerParams ```dart setSchedulerParams({required _i9.SchedulerParams new_}) { ``` Set scheduler-params. ### paraInherent #### enter ```dart enter({required _i7.InherentData data}) { ``` Enter the paras inherent. This will process bitfields and backed candidates. ### paras #### forceSetCurrentCode ```dart forceSetCurrentCode({ required _i5.Id para, required _i13.ValidationCode newCode, ``` Set the storage for the parachain validation code immediately. #### forceSetCurrentHead ```dart forceSetCurrentHead({ required _i5.Id para, required _i7.HeadData newHead, ``` Set the storage for the current parachain head data immediately. #### forceScheduleCodeUpgrade ```dart forceScheduleCodeUpgrade({ required _i5.Id para, required _i13.ValidationCode newCode, required int relayParentNumber, ``` Schedule an upgrade as if it was scheduled in the given relay parent block. #### forceNoteNewHead ```dart forceNoteNewHead({ required _i5.Id para, required _i7.HeadData newHead, ``` Note a new block head for para within the context of the current block. #### forceQueueAction ```dart forceQueueAction({required _i5.Id para}) { ``` Put a parachain directly into the next session's action queue. We can't queue it any sooner than this without going into the initializer... #### addTrustedValidationCode ```dart addTrustedValidationCode( {required _i13.ValidationCode validationCode}) { ``` Adds the validation code to the storage. The code will not be added if it is already present. Additionally, if PVF pre-checking is running for that code, it will be instantly accepted. Otherwise, the code will be added into the storage. Note that the code will be added into storage with reference count 0. This is to account the fact that there are no users for this code yet. The caller will have to make sure that this code eventually gets used by some parachain or removed from the storage to avoid storage leaks. For the latter prefer to use the `poke_unused_validation_code` dispatchable to raw storage manipulation. This function is mainly meant to be used for upgrading parachains that do not follow the go-ahead signal while the PVF pre-checking feature is enabled. #### pokeUnusedValidationCode ```dart pokeUnusedValidationCode( {required _i2.ValidationCodeHash validationCodeHash}) { ``` Remove the validation code from the storage iff the reference count is 0. This is better than removing the storage directly, because it will not remove the code that was suddenly got used by some parachain while this dispatchable was pending dispatching. #### includePvfCheckStatement ```dart includePvfCheckStatement({ required _i18.PvfCheckStatement stmt, required _i19.Signature signature, ``` Includes a statement for a PVF pre-checking vote. Potentially, finalizes the vote and enacts the results if that was the last vote before achieving the supermajority. #### forceSetMostRecentContext ```dart forceSetMostRecentContext({ required _i5.Id para, required int context, ``` Set the storage for the current parachain head data immediately. ### initializer #### forceApprove ```dart forceApprove({required int upTo}) { ``` Issue a signal to the consensus engine to forcibly act as though all parachain blocks in all relay chain blocks up to and including the given number in the current chain are valid and should be finalized. ### hrmp #### hrmpInitOpenChannel ```dart hrmpInitOpenChannel({ required _i5.Id recipient, required int proposedMaxCapacity, required int proposedMaxMessageSize, ``` Initiate opening a channel from a parachain to a given recipient with given channel parameters. - `proposed_max_capacity` - specifies how many messages can be in the channel at once. - `proposed_max_message_size` - specifies the maximum size of the messages. These numbers are a subject to the relay-chain configuration limits. The channel can be opened only after the recipient confirms it and only on a session change. #### hrmpAcceptOpenChannel ```dart hrmpAcceptOpenChannel({required _i5.Id sender}) { ``` Accept a pending open channel request from the given sender. The channel will be opened only on the next session boundary. #### hrmpCloseChannel ```dart hrmpCloseChannel({required _i2.HrmpChannelId channelId}) { ``` Initiate unilateral closing of a channel. The origin must be either the sender or the recipient in the channel being closed. The closure can only happen on a session change. #### forceCleanHrmp ```dart forceCleanHrmp({ required _i5.Id para, required int numInbound, required int numOutbound, ``` This extrinsic triggers the cleanup of all the HRMP storage items that a para may have. Normally this happens once per session, but this allows you to trigger the cleanup immediately for a specific parachain. Number of inbound and outbound channels for `para` must be provided as witness data. Origin must be the `ChannelManager`. #### forceProcessHrmpOpen ```dart forceProcessHrmpOpen({required int channels}) { ``` Force process HRMP open channel requests. If there are pending HRMP open channel requests, you can use this function to process all of those requests immediately. Total number of opening channels must be provided as witness data. Origin must be the `ChannelManager`. #### forceProcessHrmpClose ```dart forceProcessHrmpClose({required int channels}) { ``` Force process HRMP close channel requests. If there are pending HRMP close channel requests, you can use this function to process all of those requests immediately. Total number of closing channels must be provided as witness data. Origin must be the `ChannelManager`. #### hrmpCancelOpenRequest ```dart hrmpCancelOpenRequest({ required _i2.HrmpChannelId channelId, required int openRequests, ``` This cancels a pending open channel request. It can be canceled by either of the sender or the recipient for that request. The origin must be either of those. The cancellation happens immediately. It is not possible to cancel the request if it is already accepted. Total number of open requests (i.e. `HrmpOpenChannelRequestsList`) must be provided as witness data. #### forceOpenHrmpChannel ```dart forceOpenHrmpChannel({ required _i5.Id sender, required _i5.Id recipient, required int maxCapacity, required int maxMessageSize, ``` Open a channel from a `sender` to a `recipient` `ParaId`. Although opened by governance, the `max_capacity` and `max_message_size` are still subject to the Relay Chain's configured limits. Expected use is when one (and only one) of the `ParaId`s involved in the channel is governed by the system, e.g. a system parachain. Origin must be the `ChannelManager`. #### establishSystemChannel ```dart establishSystemChannel({ required _i5.Id sender, required _i5.Id recipient, ``` Establish an HRMP channel between two system chains. If the channel does not already exist, the transaction fees will be refunded to the caller. The system does not take deposits for channels between system chains, and automatically sets the message number and size limits to the maximum allowed by the network's configuration. Arguments: - `sender`: A system chain, `ParaId`. - `recipient`: A system chain, `ParaId`. Any signed origin can call this function, but *both* inputs MUST be system chains. If the channel does not exist yet, there is no fee. #### pokeChannelDeposits ```dart pokeChannelDeposits({ required _i5.Id sender, required _i5.Id recipient, ``` Update the deposits held for an HRMP channel to the latest `Configuration`. Channels with system chains do not require a deposit. Arguments: - `sender`: A chain, `ParaId`. - `recipient`: A chain, `ParaId`. Any signed origin can call this function. #### establishChannelWithSystem ```dart establishChannelWithSystem( {required _i5.Id targetSystemChain}) { ``` Establish a bidirectional HRMP channel between a parachain and a system chain. Arguments: - `target_system_chain`: A system chain, `ParaId`. The origin needs to be the parachain origin. ### parasDisputes #### forceUnfreeze ```dart forceUnfreeze() { ``` ### parasSlashing #### reportDisputeLostUnsigned ```dart reportDisputeLostUnsigned({ required _i8.DisputeProof disputeProof, required _i9.MembershipProof keyOwnerProof, ``` ### onDemand #### placeOrderAllowDeath ```dart placeOrderAllowDeath({ required BigInt maxAmount, required _i2.Id paraId, ``` Create a single on demand core order. Will use the spot price for the current block and will reap the account if needed. Parameters: - `origin`: The sender of the call, funds will be withdrawn from this account. - `max_amount`: The maximum balance to withdraw from the origin to place an order. - `para_id`: A `ParaId` the origin wants to provide blockspace for. Errors: - `InsufficientBalance`: from the Currency implementation - `QueueFull` - `SpotPriceHigherThanMaxAmount` Events: - `OnDemandOrderPlaced` #### placeOrderKeepAlive ```dart placeOrderKeepAlive({ required BigInt maxAmount, required _i2.Id paraId, ``` Same as the [`place_order_allow_death`](Self::place_order_allow_death) call , but with a check that placing the order will not reap the account. Parameters: - `origin`: The sender of the call, funds will be withdrawn from this account. - `max_amount`: The maximum balance to withdraw from the origin to place an order. - `para_id`: A `ParaId` the origin wants to provide blockspace for. Errors: - `InsufficientBalance`: from the Currency implementation - `QueueFull` - `SpotPriceHigherThanMaxAmount` Events: - `OnDemandOrderPlaced` ### registrar #### register ```dart register({ required _i2.Id id, required _i7.HeadData genesisHead, required _i8.ValidationCode validationCode, ``` Register head data and validation code for a reserved Para Id. - Arguments - `origin`: Must be called by a `Signed` origin. - `id`: The para ID. Must be owned/managed by the `origin` signing account. - `genesis_head`: The genesis head data of the parachain/thread. - `validation_code`: The initial validation code of the parachain/thread. - Deposits/Fees The account with the originating signature must reserve a deposit. The deposit is required to cover the costs associated with storing the genesis head data and the validation code. This accounts for the potential to store validation code of a size up to the `max_code_size`, as defined in the configuration pallet Anything already reserved previously for this para ID is accounted for. - Events The `Registered` event is emitted in case of success. #### forceRegister ```dart forceRegister({ required _i10.AccountId32 who, required BigInt deposit, required _i2.Id id, required _i7.HeadData genesisHead, required _i8.ValidationCode validationCode, ``` Force the registration of a Para Id on the relay chain. This function must be called by a Root origin. The deposit taken can be specified for this registration. Any `ParaId` can be registered, including sub-1000 IDs which are System Parachains. #### deregister ```dart deregister({required _i2.Id id}) { ``` Deregister a Para Id, freeing all data and returning any deposit. The caller must be Root, the `para` owner, or the `para` itself. The para must be an on-demand parachain. #### swap ```dart swap({ required _i2.Id id, required _i2.Id other, ``` Swap a lease holding parachain with another parachain, either on-demand or lease holding. The origin must be Root, the `para` owner, or the `para` itself. The swap will happen only if there is already an opposite swap pending. If there is not, the swap will be stored in the pending swaps map, ready for a later confirmatory swap. The `ParaId`s remain mapped to the same head data and code so external code can rely on `ParaId` to be a long-term identifier of a notional "parachain". However, their scheduling info (i.e. whether they're an on-demand parachain or lease holding parachain), auction information and the auction deposit are switched. #### removeLock ```dart removeLock({required _i2.Id para}) { ``` Remove a manager lock from a para. This will allow the manager of a previously locked para to deregister or swap a para without using governance. Can only be called by the Root origin or the parachain. #### reserve ```dart reserve() { ``` Reserve a Para Id on the relay chain. This function will reserve a new Para Id to be owned/managed by the origin account. The origin account is able to register head data and validation code using `register` to create an on-demand parachain. Using the Slots pallet, an on-demand parachain can then be upgraded to a lease holding parachain. - Arguments - `origin`: Must be called by a `Signed` origin. Becomes the manager/owner of the new para ID. - Deposits/Fees The origin must reserve a deposit of `ParaDeposit` for the registration. - Events The `Reserved` event is emitted in case of success, which provides the ID reserved for use. #### addLock ```dart addLock({required _i2.Id para}) { ``` Add a manager lock from a para. This will prevent the manager of a para to deregister or swap a para. Can be called by Root, the parachain, or the parachain manager if the parachain is unlocked. #### scheduleCodeUpgrade ```dart scheduleCodeUpgrade({ required _i2.Id para, required _i8.ValidationCode newCode, ``` Schedule a parachain upgrade. This will kick off a check of `new_code` by all validators. After the majority of the validators have reported on the validity of the code, the code will either be enacted or the upgrade will be rejected. If the code will be enacted, the current code of the parachain will be overwritten directly. This means that any PoV will be checked by this new code. The parachain itself will not be informed explicitly that the validation code has changed. Can be called by Root, the parachain, or the parachain manager if the parachain is unlocked. #### setCurrentHead ```dart setCurrentHead({ required _i2.Id para, required _i7.HeadData newHead, ``` Set the parachain's current head. Can be called by Root, the parachain, or the parachain manager if the parachain is unlocked. ### slots #### forceLease ```dart forceLease({ required _i2.Id para, required _i4.AccountId32 leaser, required BigInt amount, required int periodBegin, required int periodCount, ``` Just a connect into the `lease_out` call, in case Root wants to force some lease to happen independently of any other on-chain mechanism to use it. The dispatch origin for this call must match `T::ForceOrigin`. #### clearAllLeases ```dart clearAllLeases({required _i2.Id para}) { ``` Clear all leases for a Para Id, refunding any deposits back to the original owners. The dispatch origin for this call must match `T::ForceOrigin`. #### triggerOnboard ```dart triggerOnboard({required _i2.Id para}) { ``` Try to onboard a parachain that has a lease for the current lease period. This function can be useful if there was some state issue with a para that should have onboarded, but was unable to. As long as they have a lease period, we can let them onboard from here. Origin must be signed, but can be called by anyone. ### auctions #### newAuction ```dart newAuction({ required BigInt duration, required BigInt leasePeriodIndex, ``` Create a new auction. This can only happen when there isn't already an auction in progress and may only be called by the root origin. Accepts the `duration` of this auction and the `lease_period_index` of the initial lease period of the four that are to be auctioned. #### bid ```dart bid({ required BigInt para, required BigInt auctionIndex, required BigInt firstSlot, required BigInt lastSlot, required BigInt amount, ``` Make a new bid from an account (including a parachain account) for deploying a new parachain. Multiple simultaneous bids from the same bidder are allowed only as long as all active bids overlap each other (i.e. are mutually exclusive). Bids cannot be redacted. - `sub` is the sub-bidder ID, allowing for multiple competing bids to be made by (and funded by) the same account. - `auction_index` is the index of the auction to bid on. Should just be the present value of `AuctionCounter`. - `first_slot` is the first lease period index of the range to bid on. This is the absolute lease period index value, not an auction-specific offset. - `last_slot` is the last lease period index of the range to bid on. This is the absolute lease period index value, not an auction-specific offset. - `amount` is the amount to bid to be held as deposit for the parachain should the bid win. This amount is held throughout the range. #### cancelAuction ```dart cancelAuction() { ``` Cancel an in-progress auction. Can only be called by Root origin. ### crowdloan #### create ```dart create({ required BigInt index, required BigInt cap, required BigInt firstPeriod, required BigInt lastPeriod, required BigInt end, _i8.MultiSigner? verifier, ``` Create a new crowdloaning campaign for a parachain slot with the given lease period range. This applies a lock to your parachain configuration, ensuring that it cannot be changed by the parachain manager. #### contribute ```dart contribute({ required BigInt index, required BigInt value, _i10.MultiSignature? signature, ``` Contribute to a crowd sale. This will transfer some balance over to fund a parachain slot. It will be withdrawable when the crowdloan has ended and the funds are unused. #### withdraw ```dart withdraw({ required _i11.AccountId32 who, required BigInt index, ``` Withdraw full balance of a specific contributor. Origin must be signed, but can come from anyone. The fund must be either in, or ready for, retirement. For a fund to be *in* retirement, then the retirement flag must be set. For a fund to be ready for retirement, then: - it must not already be in retirement; - the amount of raised funds must be bigger than the *free* balance of the account; - and either: - the block number must be at least `end`; or - the current lease period must be greater than the fund's `last_period`. In this case, the fund's retirement flag is set and its `end` is reset to the current block number. - `who`: The account whose contribution should be withdrawn. - `index`: The parachain to whose crowdloan the contribution was made. #### refund ```dart refund({required BigInt index}) { ``` Automatically refund contributors of an ended crowdloan. Due to weight restrictions, this function may need to be called multiple times to fully refund all users. We will refund `RemoveKeysLimit` users at a time. Origin must be signed, but can come from anyone. #### dissolve ```dart dissolve({required BigInt index}) { ``` Remove a fund after the retirement period has ended and all funds have been returned. #### edit ```dart edit({ required BigInt index, required BigInt cap, required BigInt firstPeriod, required BigInt lastPeriod, required BigInt end, _i8.MultiSigner? verifier, ``` Edit the configuration for an in-progress crowdloan. Can only be called by Root origin. #### addMemo ```dart addMemo({ required _i2.Id index, required List memo, ``` Add an optional memo to an existing crowdloan contribution. Origin must be Signed, and the user must have contributed to the crowdloan. #### poke ```dart poke({required _i2.Id index}) { ``` Poke the fund into `NewRaise` Origin must be Signed, and the fund has non-zero raise. #### contributeAll ```dart contributeAll({ required BigInt index, _i10.MultiSignature? signature, ``` Contribute your entire balance to a crowd sale. This will transfer the entire balance of a user over to fund a parachain slot. It will be withdrawable when the crowdloan has ended and the funds are unused. ### coretime #### requestCoreCount ```dart requestCoreCount({required int count}) { ``` Request the configuration to be updated with the specified number of cores. Warning: Since this only schedules a configuration update, it takes two sessions to come into effect. - `origin`: Root or the Coretime Chain - `count`: total number of cores #### requestRevenueAt ```dart requestRevenueAt({required int when}) { ``` Request to claim the instantaneous coretime sales revenue starting from the block it was last claimed until and up to the block specified. The claimed amount value is sent back to the Coretime chain in a `notify_revenue` message. At the same time, the amount is teleported to the Coretime chain. #### assignCore ```dart assignCore({ required int core, required int begin, required List<_i3.Tuple2<_i4.CoreAssignment, _i5.PartsOf57600>> assignment, int? endHint, ``` Receive instructions from the `ExternalBrokerOrigin`, detailing how a specific core is to be used. Parameters: -`origin`: The `ExternalBrokerOrigin`, assumed to be the coretime chain. -`core`: The core that should be scheduled. -`begin`: The starting blockheight of the instruction. -`assignment`: How the blockspace should be utilised. -`end_hint`: An optional hint as to when this particular set of instructions will end. ### stateTrieMigration #### controlAutoMigration ```dart controlAutoMigration({_i3.MigrationLimits? maybeConfig}) { ``` Control the automatic migration. The dispatch origin of this call must be [`Config::ControlOrigin`]. #### continueMigrate ```dart continueMigrate({ required _i3.MigrationLimits limits, required int realSizeUpper, required _i2.MigrationTask witnessTask, ``` Continue the migration for the given `limits`. The dispatch origin of this call can be any signed account. This transaction has NO MONETARY INCENTIVES. calling it will not reward anyone. Albeit, Upon successful execution, the transaction fee is returned. The (potentially over-estimated) of the byte length of all the data read must be provided for up-front fee-payment and weighing. In essence, the caller is guaranteeing that executing the current `MigrationTask` with the given `limits` will not exceed `real_size_upper` bytes of read data. The `witness_task` is merely a helper to prevent the caller from being slashed or generally trigger a migration that they do not intend. This parameter is just a message from caller, saying that they believed `witness_task` was the last state of the migration, and they only wish for their transaction to do anything, if this assumption holds. In case `witness_task` does not match, the transaction fails. Based on the documentation of [`MigrationTask::migrate_until_exhaustion`], the recommended way of doing this is to pass a `limit` that only bounds `count`, as the `size` limit can always be overwritten. #### migrateCustomTop ```dart migrateCustomTop({ required List> keys, required int witnessSize, ``` Migrate the list of top keys by iterating each of them one by one. This does not affect the global migration process tracker ( [`MigrationProcess`] ), and should only be used in case any keys are leftover due to a bug. #### migrateCustomChild ```dart migrateCustomChild({ required List root, required List> childKeys, required int totalSize, ``` Migrate the list of child keys by iterating each of them one by one. All of the given child keys must be present under one `child_root`. This does not affect the global migration process tracker ( [`MigrationProcess`] ), and should only be used in case any keys are leftover due to a bug. #### setSignedMaxLimits ```dart setSignedMaxLimits({required _i3.MigrationLimits limits}) { ``` Set the maximum limit of the signed migration. #### forceSetProgress ```dart forceSetProgress({ required _i6.Progress progressTop, required _i6.Progress progressChild, ``` Forcefully set the progress the running migration. This is only useful in one case: the next key to migrate is too big to be migrated with a signed account, in a parachain context, and we simply want to skip it. A reasonable example of this would be `:code:`, which is both very expensive to migrate, and commonly used, so probably it is already migrated. In case you mess things up, you can also, in principle, use this to reset the migration process. ### xcmPallet #### send ```dart send({ required _i5.VersionedLocation dest, required _i16.VersionedXcm message, ``` #### teleportAssets ```dart teleportAssets({ required _i5.VersionedLocation dest, required _i5.VersionedLocation beneficiary, required _i18.VersionedAssets assets, required int feeAssetItem, ``` Teleport some assets from the local chain to some destination chain. **This function is deprecated: Use `limited_teleport_assets` instead.** Fee payment on the destination side is made from the asset in the `assets` vector of index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited, with all fees taken as needed from the asset. - `origin`: Must be capable of withdrawing the `assets` and executing XCM. - `dest`: Destination context for the assets. Will typically be `[Parent, Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from relay to parachain. - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be an `AccountId32` value. - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the `dest` chain. - `fee_asset_item`: The index into `assets` of the item which should be used to pay fees. #### reserveTransferAssets ```dart reserveTransferAssets({ required _i5.VersionedLocation dest, required _i5.VersionedLocation beneficiary, required _i18.VersionedAssets assets, required int feeAssetItem, ``` Transfer some assets from the local chain to the destination chain through their local, destination or remote reserve. `assets` must have same reserve location and may not be teleportable to `dest`. - `assets` have local reserve: transfer assets to sovereign account of destination chain and forward a notification XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. - `assets` have destination reserve: burn local assets and forward a notification to `dest` chain to withdraw the reserve assets from this chain's sovereign account and deposit them to `beneficiary`. - `assets` have remote reserve: burn local assets, forward XCM to reserve chain to move reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. **This function is deprecated: Use `limited_reserve_transfer_assets` instead.** Fee payment on the destination side is made from the asset in the `assets` vector of index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited, with all fees taken as needed from the asset. - `origin`: Must be capable of withdrawing the `assets` and executing XCM. - `dest`: Destination context for the assets. Will typically be `[Parent, Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from relay to parachain. - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be an `AccountId32` value. - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the `dest` (and possibly reserve) chains. - `fee_asset_item`: The index into `assets` of the item which should be used to pay fees. #### execute ```dart execute({ required _i19.VersionedXcm message, required _i7.Weight maxWeight, ``` Execute an XCM message from a local, signed, origin. An event is deposited indicating whether `msg` could be executed completely or only partially. No more than `max_weight` will be used in its attempted execution. If this is less than the maximum amount of weight that the message could take to be executed, then no execution attempt will be made. #### forceXcmVersion ```dart forceXcmVersion({ required _i20.Location location, required int version, ``` Extoll that a particular destination can be communicated with through a particular version of XCM. - `origin`: Must be an origin specified by AdminOrigin. - `location`: The destination that is being described. - `xcm_version`: The latest version of XCM that `location` supports. #### forceDefaultXcmVersion ```dart forceDefaultXcmVersion({int? maybeXcmVersion}) { ``` Set a safe XCM version (the version that XCM should be encoded with if the most recent version a destination can accept is unknown). - `origin`: Must be an origin specified by AdminOrigin. - `maybe_xcm_version`: The default XCM encoding version, or `None` to disable. #### forceSubscribeVersionNotify ```dart forceSubscribeVersionNotify( {required _i5.VersionedLocation location}) { ``` Ask a location to notify us regarding their XCM version and any changes to it. - `origin`: Must be an origin specified by AdminOrigin. - `location`: The location to which we should subscribe for XCM version notifications. #### forceUnsubscribeVersionNotify ```dart forceUnsubscribeVersionNotify( {required _i5.VersionedLocation location}) { ``` Require that a particular destination should no longer notify us regarding any XCM version changes. - `origin`: Must be an origin specified by AdminOrigin. - `location`: The location to which we are currently subscribed for XCM version notifications which we no longer desire. #### limitedReserveTransferAssets ```dart limitedReserveTransferAssets({ required _i5.VersionedLocation dest, required _i5.VersionedLocation beneficiary, required _i18.VersionedAssets assets, required int feeAssetItem, required _i21.WeightLimit weightLimit, ``` Transfer some assets from the local chain to the destination chain through their local, destination or remote reserve. `assets` must have same reserve location and may not be teleportable to `dest`. - `assets` have local reserve: transfer assets to sovereign account of destination chain and forward a notification XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. - `assets` have destination reserve: burn local assets and forward a notification to `dest` chain to withdraw the reserve assets from this chain's sovereign account and deposit them to `beneficiary`. - `assets` have remote reserve: burn local assets, forward XCM to reserve chain to move reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Fee payment on the destination side is made from the asset in the `assets` vector of index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight is needed than `weight_limit`, then the operation will fail and the sent assets may be at risk. - `origin`: Must be capable of withdrawing the `assets` and executing XCM. - `dest`: Destination context for the assets. Will typically be `[Parent, Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from relay to parachain. - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be an `AccountId32` value. - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the `dest` (and possibly reserve) chains. - `fee_asset_item`: The index into `assets` of the item which should be used to pay fees. - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase. #### limitedTeleportAssets ```dart limitedTeleportAssets({ required _i5.VersionedLocation dest, required _i5.VersionedLocation beneficiary, required _i18.VersionedAssets assets, required int feeAssetItem, required _i21.WeightLimit weightLimit, ``` Teleport some assets from the local chain to some destination chain. Fee payment on the destination side is made from the asset in the `assets` vector of index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight is needed than `weight_limit`, then the operation will fail and the sent assets may be at risk. - `origin`: Must be capable of withdrawing the `assets` and executing XCM. - `dest`: Destination context for the assets. Will typically be `[Parent, Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from relay to parachain. - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be an `AccountId32` value. - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the `dest` chain. - `fee_asset_item`: The index into `assets` of the item which should be used to pay fees. - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase. #### forceSuspension ```dart forceSuspension({required bool suspended}) { ``` Set or unset the global suspension state of the XCM executor. - `origin`: Must be an origin specified by AdminOrigin. - `suspended`: `true` to suspend, `false` to resume. #### transferAssets ```dart transferAssets({ required _i5.VersionedLocation dest, required _i5.VersionedLocation beneficiary, required _i18.VersionedAssets assets, required int feeAssetItem, required _i21.WeightLimit weightLimit, ``` Transfer some assets from the local chain to the destination chain through their local, destination or remote reserve, or through teleports. Fee payment on the destination side is made from the asset in the `assets` vector of index `fee_asset_item` (hence referred to as `fees`), up to enough to pay for `weight_limit` of weight. If more weight is needed than `weight_limit`, then the operation will fail and the sent assets may be at risk. `assets` (excluding `fees`) must have same reserve location or otherwise be teleportable to `dest`, no limitations imposed on `fees`. - for local reserve: transfer assets to sovereign account of destination chain and forward a notification XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. - for destination reserve: burn local assets and forward a notification to `dest` chain to withdraw the reserve assets from this chain's sovereign account and deposit them to `beneficiary`. - for remote reserve: burn local assets, forward XCM to reserve chain to move reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. - for teleports: burn local assets and forward XCM to `dest` chain to mint/teleport assets and deposit them to `beneficiary`. - `origin`: Must be capable of withdrawing the `assets` and executing XCM. - `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain. - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be an `AccountId32` value. - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the `dest` (and possibly reserve) chains. - `fee_asset_item`: The index into `assets` of the item which should be used to pay fees. - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase. #### claimAssets ```dart claimAssets({ required _i18.VersionedAssets assets, required _i5.VersionedLocation beneficiary, ``` Claims assets trapped on this pallet because of leftover assets during XCM execution. - `origin`: Anyone can call this extrinsic. - `assets`: The exact assets that were trapped. Use the version to specify what version was the latest when they were trapped. - `beneficiary`: The location/account where the claimed assets will be deposited. #### transferAssetsUsingTypeAndThen ```dart transferAssetsUsingTypeAndThen({ required _i5.VersionedLocation dest, required _i18.VersionedAssets assets, required _i22.TransferType assetsTransferType, required _i10.VersionedAssetId remoteFeesId, required _i22.TransferType feesTransferType, required _i16.VersionedXcm customXcmOnDest, required _i21.WeightLimit weightLimit, ``` Transfer assets from the local chain to the destination chain using explicit transfer types for assets and fees. `assets` must have same reserve location or may be teleportable to `dest`. Caller must provide the `assets_transfer_type` to be used for `assets`: - `TransferType::LocalReserve`: transfer assets to sovereign account of destination chain and forward a notification XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. - `TransferType::DestinationReserve`: burn local assets and forward a notification to `dest` chain to withdraw the reserve assets from this chain's sovereign account and deposit them to `beneficiary`. - `TransferType::RemoteReserve(reserve)`: burn local assets, forward XCM to `reserve` chain to move reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Typically the remote `reserve` is Asset Hub. - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to mint/teleport assets and deposit them to `beneficiary`. On the destination chain, as well as any intermediary hops, `BuyExecution` is used to buy execution using transferred `assets` identified by `remote_fees_id`. Make sure enough of the specified `remote_fees_id` asset is included in the given list of `assets`. `remote_fees_id` should be enough to pay for `weight_limit`. If more weight is needed than `weight_limit`, then the operation will fail and the sent assets may be at risk. `remote_fees_id` may use different transfer type than rest of `assets` and can be specified through `fees_transfer_type`. The caller needs to specify what should happen to the transferred assets once they reach the `dest` chain. This is done through the `custom_xcm_on_dest` parameter, which contains the instructions to execute on `dest` as a final step. This is usually as simple as: `Xcm(vec![DepositAsset { assets: Wild(AllCounted(assets.len())), beneficiary }])`, but could be something more exotic like sending the `assets` even further. - `origin`: Must be capable of withdrawing the `assets` and executing XCM. - `dest`: Destination context for the assets. Will typically be `[Parent, Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from relay to parachain, or `(parents: 2, (GlobalConsensus(..), ..))` to send from parachain across a bridge to another ecosystem destination. - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the `dest` (and possibly reserve) chains. - `assets_transfer_type`: The XCM `TransferType` used to transfer the `assets`. - `remote_fees_id`: One of the included `assets` to be used to pay fees. - `fees_transfer_type`: The XCM `TransferType` used to transfer the `fees` assets. - `custom_xcm_on_dest`: The XCM to be executed on `dest` chain as the last step of the transfer, which also determines what happens to the assets on the destination chain. - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase. ### messageQueue #### reapPage ```dart reapPage({ required _i2.AggregateMessageOrigin messageOrigin, required int pageIndex, ``` Remove a page which has no more messages remaining to be processed or is stale. #### executeOverweight ```dart executeOverweight({ required _i2.AggregateMessageOrigin messageOrigin, required int page, required int index, required _i10.Weight weightLimit, ``` Execute an overweight message. Temporary processing errors will be propagated whereas permanent errors are treated as success condition. - `origin`: Must be `Signed`. - `message_origin`: The origin from which the message to be executed arrived. - `page`: The page in the queue in which the message to be executed is sitting. - `index`: The index into the queue of the message to be executed. - `weight_limit`: The maximum amount of weight allowed to be consumed in the execution of the message. Benchmark complexity considerations: O(index + weight\_limit). ### assetRate #### create ```dart create({ required _i2.VersionedLocatableAsset assetKind, required _i3.FixedU128 rate, ``` Initialize a conversion rate to native balance for the given asset. - Complexity - O(1) #### update ```dart update({ required _i2.VersionedLocatableAsset assetKind, required _i3.FixedU128 rate, ``` Update the conversion rate to native balance for the given asset. - Complexity - O(1) #### remove ```dart remove({required _i2.VersionedLocatableAsset assetKind}) { ``` Remove an existing conversion rate to native balance for the given asset. - Complexity - O(1) ### beefy #### reportEquivocation ```dart reportEquivocation({ required _i7.DoubleVotingProof equivocationProof, required _i8.MembershipProof keyOwnerProof, ``` Report voter equivocation/misbehavior. This method will verify the equivocation proof and validate the given key ownership proof against the extracted offender. If both are valid, the offence will be reported. #### reportEquivocationUnsigned ```dart reportEquivocationUnsigned({ required _i7.DoubleVotingProof equivocationProof, required _i8.MembershipProof keyOwnerProof, ``` Report voter equivocation/misbehavior. This method will verify the equivocation proof and validate the given key ownership proof against the extracted offender. If both are valid, the offence will be reported. This extrinsic must be called unsigned and it is expected that only block authors will call it (validated in `ValidateUnsigned`), as such if the block author is defined it will be defined as the equivocation reporter. #### setNewGenesis ```dart setNewGenesis({required int delayInBlocks}) { ``` Reset BEEFY consensus by setting a new BEEFY genesis at `delay_in_blocks` blocks in the future. Note: `delay_in_blocks` has to be at least 1. ### Raw metadata ### Decoder ```dart // decode metadata final DecodedMetadata decodedMetadata = MetadataDecoder.instance.decode('0x6d657.....790b807d0b'); // create ChainInfo from metadata final ChainInfo chainInfo = ChainInfo.fromMetadata(decodedMetadata); final String extrinsicHex = '0x990403......a2f9e184'; // Create extrinsics input final input = Input.fromHex(extrinsicHex); // decode extrinsic final dynamic decoded = ExtrinsicsCodec(chainInfo: chainInfo).decode(input); ``` ### Encoder ```dart // decode metadata final DecodedMetadata decodedMetadata = MetadataDecoder.instance.decode('0x6d657.....790b807d0b'); // create ChainInfo from metadata final ChainInfo chainInfo = ChainInfo.fromMetadata(decodedMetadata); // Create Output final output = HexOutput(); final Map extrinsicsMap = {'version': 4, 'signature': ....... }; // encode extrinsic ExtrinsicsCodec(chainInfo: chainInfo).encodeTo(extrinsicsMap, output); // encoded extrinsics Hex final extrinsicsHex = output.toString(); ``` ### Type generation ### # Overview One of the most important aspects of working with blockchain is the ability of signing transactions. The Polkadart Keyring package provide a convenient way to manage key pairs and sign transactions. #### Installation Add the following to your `pubspec.yaml` file: ```yaml [pubspec.yaml] dependencies: polkadart_keyring: ^0.7.2 ``` Get the dependencies by running: ```bash dart pub get ``` #### Sample usage ```dart [demo.dart] Future main(List arguments) async { final keyring = Keyring(); final keyPair = await keyring.fromUri('//Alice'); print('Alice address: ${keyPair.address}'); } ``` You will get the following output: ```dart Alice address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY ``` The keyring is a very powerful tool and we will show you what you can do with it in the following sections. # Keypair ## Creating keypair You can create a keypair in multiple ways, using BIP39 mnemonic, URI, seed, in the following examples we will create a keypair for Alice using each of these methods. ### From mnemonic ```dart final keyring = Keyring(); final mnemonic = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice'; final keypair = await keyring.fromMnemonic(mnemonic); print('Alice address: ${keypair.address}'); ``` ### From URI ```dart final keyring = Keyring(); final keypair = await keyring.fromUri('//Alice'); print('Alice address: ${keyPair.address}'); ``` ### From Seed ```dart final keyring = Keyring(); final keypair = await keyring.fromSeed(decodeHex('0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a')); print('Alice address: ${keypair.address}'); ``` ## Manage keypair The keyring provides a way to manage keypairs, you can add, retrieve, and remove keypairs. ### Add keypair ```dart final keyring = Keyring(); final keyPair1 = KeyPair.sr25519.fromSeed( Uint8List.fromList('12345678901234567890123456789012'.codeUnits)); final keyPair2 = KeyPair.sr25519.fromSeed(Uint8List.fromList(hex.decode( '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60')); // Add keyPair1 to keyring keyring.add(keyPair1); final address1 = keyPair1.address; final address2 = keyPair2.address; // Add keyPair2 to keyring keyring.add(keyPair2); final addresses = keyring.addresses; ``` ### Retrieve keypair ```dart final keyring = Keyring(); final keyPair1 = KeyPair.sr25519.fromSeed( Uint8List.fromList('12345678901234567890123456789012'.codeUnits)); final publicKey1 = keyPair1.publicKey.bytes; // Add keyPair1 to key keyring.add(keyPair1); final keyPair = keyring.getByPublicKey(publicKey1); final nonExistentKeyPair = keyring.getByPublicKey([1, 2, 3]); ``` ### Remove keypair ```dart final keyring = Keyring(); final keyPair1 = KeyPair.sr25519.fromSeed( Uint8List.fromList('12345678901234567890123456789012'.codeUnits)); // Add keyPair1 and keyPair2 to keyring keyring.add(keyPair1); keyring.add(keyPair2); final address1 = keyPair1.address; keyring.remove(address1); final nonExistentKeyPair = keyring.getByAddress(address1); ``` # Signing and Verifying ```dart final keyring = Keyring(); final keyPair2 = KeyPair.sr25519.fromSeed(Uint8List.fromList(hex.decode( '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60')); keyring.add(keyPair2); final kp = keyring.getByPublicKey(keyPair2.publicKey.bytes); final signature = kp.sign(message); final isVerified = kp.verify(message, signature); ``` # SS58 Address Format The SS58 address format is the standard for encoding and decoding addresses in the Polkadot ecosystem. It is based on the Bitcoin Base-58-check encoding, with a few modifications designed for the needs of Polkadot-based chains. With this format it is possible to identity to which network a given address belongs, and to encode and decode addresses for different networks. ## Installation Add the following to your `pubspec.yaml` file: ```yaml [pubspec.yaml] dependencies: ss58: ^2.0.0 ``` Get the dependencies by running: ```bash dart pub get ``` ## Usage ### Decoding an Address ```dart final address = Address.decode('1zugcag7cJVBtVRnFxv5Qftn7xKAnR6YJ9x4x3XLgGgmNnS'); ``` ### Registry Info by Prefix ```dart // get registry info of given `prefix` final polkadotRegistry = Codec.registry.getByPrefix(0); print('polkadot registry: $polkadotRegistry'); ``` ### Decoding Substrate Address ```dart // decoding substrate address final String originalEncodedAddress = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; final List decodedBytes = Codec.fromNetwork('substrate').decode(originalEncodedAddress); print('Substrate address bytes: $decodedBytes'); ``` ### Encoding bytes to produce address ```dart // Encoding the decodedBytes to produce back encodedAddress. final int substrateAddressPrefix = 42; final encodedAddress = Codec(substrateAddressPrefix).encode(decodedBytes); print(encodedAddress); ``` # Getting Started with ink! Smart Contracts The Polkadart ink! packages provide tools for interacting with ink! smart contracts from Dart and Flutter applications. You can use these packages to parse contract metadata, generate type-safe bindings, deploy contracts and interact with them. #### Installation Add the following to your `pubspec.yaml` file: ```yaml [pubspec.yaml] dependencies: ink_abi: ^1.0.0 ink_cli: ^1.0.0 ``` Get the dependencies by running: ```bash dart pub get ``` #### Sample usage ```dart [demo.dart] Future main() async { // Parse contract metadata final json = jsonDecode(File('flipper.json').readAsStringSync()); final abi = InkAbi(json); // Generate Dart bindings final generator = TypeGenerator( abiFilePath: 'flipper.json', fileOutput: FileOutput('generated_flipper.dart') ); generator.generate(); print('Contract ABI loaded and bindings generated'); } ``` This will load the Flipper contract metadata and generate type-safe Dart bindings for interacting with the contract. The ink! packages allow you to work with smart contracts in a type-safe way, making contract interactions more reliable and maintainable. # Ink ABI Dart library for handling ink! smart contract metadata. ## Requirements Ensure that your environment meets the following requirements: - Dart `>=3.8.0` - Compatible with Flutter and pure Dart projects ## Installation To install `ink_abi` in your Dart project, add the following dependency to your `pubspec.yaml` file: ```yaml dependencies: ink_abi: ^1.0.0 ``` Then, run: ```sh dart pub get ``` or ```sh flutter pub get ``` ## ABI Support Ink! smart contract metadata serves as a blueprint for how a contract is structured and interacts with external systems. It defines storage layouts, executable methods, and data types, ensuring seamless communication between the contract and its callers. This makes it easier for developers to build, analyze, and integrate contracts efficiently across different platforms. Currently, `ink_abi` supports metadata versions: **v3, v4, v5**. ## Example Usage Below is an example demonstrating how to load and parse the metadata for a Flipper smart contract: ```dart void main() { // Read and parse the Flipper contract metadata final json = jsonDecode(File('flipper.json').readAsStringSync()); // Initialize InkAbi with Flipper metadata final InkAbi inkAbi = InkAbi(json); } ``` This initializes an `InkAbi` instance using metadata from the Flipper contract JSON file, allowing interaction with the Flipper contract's flip and get methods in a structured manner. # Ink CLI `ink_cli` is a Dart library designed to simplify the process of generating Dart classes and methods for interacting with ink! smart contracts. This documentation will show you how to use ink\_cli with examples from a simple Flipper contract. ## Requirements Before using `ink_cli`, ensure you have: - Dart `>=3.8.0` - `polkadart` & `polkadart_keyring` for blockchain interaction - `ink_abi` for contract ABI parsing ## Installation Add the following dependencies to your `pubspec.yaml` file: ```yaml dependencies: ink_abi: ^1.0.0 ink_cli: ^1.0.0 polkadart: ^1.0.0 polkadart_keyring: ^0.7.2 ``` Then, run: ```sh dart pub get ``` or ```sh flutter pub get ``` ## Generating Contract Classes The core functionality of ink\_cli is generating type-safe Dart classes from ink! contract metadata. Here's how to generate contract classes: ```dart void generateTypes() { final fileOutput = FileOutput('generated_flipper.dart'); final generator = TypeGenerator( abiFilePath: 'flipper.json', fileOutput: fileOutput); generator.generate(); fileOutput.write(); } ``` ## Deploying Contracts Once classes are generated, you can deploy contracts using the generated code: ```dart Future deployContract() async { final polkadart = Provider.fromUri(Uri.parse('ws://127.0.0.1:9944')); final alice = await KeyPair.sr25519.fromUri('//Alice'); final contract = Contract( provider: polkadart, address: Uint8List.fromList(alice.publicKey.bytes.toList()), ); final contractFile = File('flipper.wasm'); final InstantiateRequest result = await contract.new_contract( init_value: true, code: await contractFile.readAsBytes(), keyPair: alice, ); print('Contract address: 0x${encodeHex(result.contractAddress)}'); return result; } ``` ## Contract Interaction Here's an example of interacting with a deployed contract: ```dart void callContract(List address) async { final polkadart = Provider.fromUri(Uri.parse('ws://127.0.0.1:9944')); final contract = Contract( provider: polkadart, address: Uint8List.fromList(address), ); // Call the get method print('Get value: ${await contract.get()}'); } ``` ## Features ink\_cli provides several key features: - **Automated Code Generation**: No need to manually write contract interaction code. - **Seamless Integration**: Works with `polkadart` for blockchain communication. - **Scalable & Extendable**: Supports multiple versions of ink! metadata. # Guides Overview Welcome to the Polkadart guides! These tutorials walk you through common blockchain development tasks with practical, real-world examples. Each guide is designed to be standalone, so you can jump directly to the topics that interest you. ::callout{type="tip"} **New to Polkadart?** Start with [Installation](https://polkadart.dev/getting-started/installation) and [Type Generator](https://polkadart.dev/getting-started/type-generator) first. :: ## Available Guides ::card ```text ``` ## What You'll Learn Through these guides, you'll learn how to: - πŸ”— **Connect** to any Substrate-based blockchain - πŸ“¦ **Query** and decode storage data - πŸ’Έ **Send** transactions and transfers - πŸ‘€ **Subscribe** to real-time blockchain events - πŸ“ **Interact** with smart contracts - πŸ” **Sign** and verify messages ## Prerequisites Before starting these guides, make sure you have: 1. Dart SDK installed (version 3.8.0 or higher) 2. Polkadart packages added to your project 3. Generated types for your target blockchain Need help with setup? Check our [Getting Started](https://polkadart.dev/getting-started/installation) section. ## Getting Help If you run into issues: - Check our [API documentation](https://polkadart.dev/api/overview) - Visit our [GitHub repository](https://github.com/leonardocustodio/polkadart){rel="nofollow"} - Join the community on [Matrix](https://matrix.to/#/#polkadart\:matrix.org){rel="nofollow"} :: # Interacting with ink! Smart Contracts Using Polkadart This guide demonstrates how to interact with ink! smart contracts using Polkadart. In this example, we use the Flipper contract to walk through the entire processβ€”from setting up your environment to deploying and interacting with your contract. ## Table of Contents - [Overview](https://polkadart.dev/#overview) - [Prerequisites](https://polkadart.dev/#prerequisites) - [Installation](https://polkadart.dev/#installation) - [Generating Contract Bindings](https://polkadart.dev/#generating-contract-bindings) - [Deploying the Contract](https://polkadart.dev/#deploying-the-contract) - [Interacting with the Contract](https://polkadart.dev/#interacting-with-the-contract) - [Conclusion](https://polkadart.dev/#conclusion) ## Overview In this guide you will: - Set up your ink! development environment. - Create, build, and deploy a smart contract. - Generate Dart bindings for your contract using the `ink_cli` package. - Interact with your deployed contract using Polkadart libraries. By following these steps, you’ll gain a clear understanding of how to work with ink! smart contracts in a Dart environment. ## Prerequisites Before you begin, ensure you have completed the following: ::step ## Set Up Your ink! Development Environment Install Rust and the necessary dependencies by following the [ink! setup guide](https://use.ink/getting-started/setup){rel="nofollow"}. :::step ## Create an ink! Project Initialize and test a basic Flipper contract using the [ink! project creation guide](https://use.ink/getting-started/creating-an-ink-project){rel="nofollow"}. ::::step ## Build Your Contract Compile your smart contract to Wasm as detailed in the [building your contract guide](https://use.ink/getting-started/building-your-contract){rel="nofollow"}. :::::step ## Run a Substrate Node Start a local development node by following the [running Substrate guide](https://use.ink/getting-started/running-substrate){rel="nofollow"}. For more detailed instructions, refer to the [official ink! documentation](https://use.ink/){rel="nofollow"}. ## Installation To begin interacting with your smart contract using Polkadart, add the required dependencies to your `pubspec.yaml` file: ```yaml dependencies: ink_abi: ^1.0.0 ink_cli: ^1.0.0 polkadart: ^1.0.0 polkadart_keyring: ^0.7.2 ``` After updating the file, run the following command in your terminal to install the packages: This command installs all dependencies needed to proceed with generating contract bindings and interacting with your contract. ```bash dart pub get ``` # Generating Contract Bindings To enable communication with your smart contract, you need to generate Dart bindings from your contract’s ABI. ## Step 1: Create the Binding Generation Script Create a Dart file named `generate_contract.dart` and add the following code: ```dart import 'package:ink_cli/ink_cli.dart'; final fileOutput = FileOutput('generated_flipper.dart'); final generator = TypeGenerator(abiFilePath: 'flipper.json', fileOutput: fileOutput); generator.generate(); fileOutput.write(); ``` This script reads the ABI from flipper.json and generates a Dart file (generated\_flipper.dart) containing all necessary bindings. ## Step 2: Run the Binding Generation Script Execute the following command to generate the bindings: ```bash dart run generate_contract.dart ``` Once the script completes, you will have a new file (generated\_flipper.dart) ready for interacting with your contract. # Deploying the Contract After generating the bindings, the next step is to deploy your contract to a local Substrate node and interact with it. ## Necessary imports ```dart ``` ## Create the connection and load account ```dart Future main() async { // Connect to the local Substrate node final provider = Provider.fromUri(Uri.parse('ws://127.0.0.1:9944')); // Load the account (e.g., Alice) to sign transactions final alice = await KeyPair.sr25519.fromUri('//Alice'); ... ... ... ``` ## Create contract deployable object ```dart // Create a deployer object to handle contract deployment final contractDeployer = await ContractDeployer.from(provider: provider); ``` ## Deploy the contract with the help of generated constructors ```dart // Deploy the contract using the Wasm file and initial parameter (init_value) final InstantiateRequest result = await Contract.new_contract( init_value: true, keyPair: alice, code: await File('flipper.wasm').readAsBytes(), deployer: contractDeployer, ); // Deployed Contract Address final contractAddress = result.contractAddress; print('Contract deployed at: $contractAddress'); ``` ## Create a contract object from the returned contract - address ```dart // Initialize the contract instance with the deployed contract address final deployedContract = Contract( provider: provider, address: Uint8List.fromList(alice.publicKey.bytes.toList()), contractAddress: Uint8List.fromList(result.contractAddress), ); ``` # Interacting with the contract ## Read the state of the contract - Get the value of the flipper contract ```dart // Allow time for the deployment to process await Future.delayed(Duration(seconds: 1)); // Query the initial value stored in the contract final initialValue = await deployedContract.get(); print('Initial value: $initialValue'); ``` # Mutating the state ```dart // Contract mutator object final mutator = await ContractMutator.fromProvider(provider: provider); ``` ## Mutate the value of the contract ```dart final executionResult = await deployedContract.flip( keyPair: alice, mutator: mutator, ); // Wait for 20 seconds to let the changes be in the block await Future.delayed(Duration(seconds: 20)); final flippedValue = await deployedContract.get(); print('Value after doing flip: $flippedValue'); ``` # Understanding the Script - **Connecting to the Node:** - The Provider.fromUri method connects to a local Substrate node running at ws\://127.0.0.1:9944. - **Account Management:** - The script loads Alice’s key pair using KeyPair.sr25519.fromUri('//Alice'). This account is used to sign the transaction for deploying the contract. - **Contract Deployment:** - A deployer object is created and used with the Contract.new\_contract method. This method takes the Wasm bytecode from flipper.wasm, an initial parameter (init\_value), and the key pair to deploy the contract. - **Contract Interaction:** - After deployment, the script creates a contract instance with the generated address and queries the contract’s initial state using the get() method. # Conclusion ## In this guide, we covered how to interact with ink! smart contracts using Polkadart by: - Getting up your development environment. - Installing the required Dart dependencies. - Generating Dart bindings from your contract’s ABI. - Deploying and interacting with your contract on a local Substrate node. You can now experiment further by modifying the Flipper contract, exploring additional functionalities, or even deploying your contract on a testnet. For the latest updates and advanced features, always refer to the [official ink! documentation](https://use.ink/){rel="nofollow"}. ::::: :::: ::: :: # Listen to balances Knowing the balance of a wallet is a fundamental operation in any blockchain application. You can query the balance of each wallet on-demand, but it is more efficient to listen to balance changes in real-time. The example below shows how to retrieve and subscribe to balance updates. #### Get balance using generated code ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); // Account from SS58 address final account = Address.decode('19t9Q2ay58hMDaeg6eeBhqmHsRnc2jDMV3cYYw9zbc59HLj'); // Retrieve Account Balance final accountInfo = await polkadot.query.system.account(account.pubkey); print('Balance: ${accountInfo.data.free}'); // Output: Balance: 20109379543332 } ``` #### Listen to balance changes ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final account = Address.decode('5HHJX1Nq3f6QH8HGxnCemyBQ4eAcpUwmKoLMw5AvboavPfrh'); final storageKey = polkadot.query.system.accountKey(account.pubkey); polkadot.rpc.state.subscribeStorage([storageKey], (storage) { final accountInfo = AccountInfo.decode(Input.fromBytes(storage.changes[0].value!)); print('Account Info: ${accountInfo.toJson()}'); }); // Output: Account Info: {nonce: 28, consumers: 1, providers: 1, sufficients: 0, data: {free: 1010064392159, reserved: 50611660000000, frozen: 0, flags: 170141183460469231731687303715884105728}} ``` # Listen to new blocks Blocks are the fundamental unit of time in a blockchain. They are created by the network every few seconds and contain a list of transactions that have been validated by the network. Many applications need to listen to new blocks as they are created in order to update their state or trigger some action. In the following snippet we are showing you how to listen to new blocks using Polkadart. ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final subscription = await provider.subscribe('chain_subscribeNewHeads', []); subscription.stream.forEach((response) { print('New head: ${response.result}'); }); } ``` ```bash frame="none" New head: {parentHash: 0x3754eb0ce660717868bf38b795075fcfc6aa203adb0929155f8cda241f5434b3, number: 0x1622278, stateRoot: 0x23f76264620c5c018dad925bcce8d881eea8e0388dd50c2f9214bcc1dc550ea3, extrinsicsRoot: 0x56e3a2a8642b6f651953eb57893c8fbf2e5500cf8a568a310f325cb9a52e9639, digest: {logs: [0x0642414245b501039f010000109b301100000000ee1dd8472584ffa6f214c229215366b3ad0e29cb71529e9835ac63114ac05877e44938bdfa3bb2b913364980d40db0523187406a3b05b534a5f1ef04516f960ea3625d9ae35b952e97df444965bd65ee569d0454a99aceca6dc5bac25091470a, 0x0442454546840335617d959f20181a3f85b44ab8055c51d5d2c5a0cfd5317466ab4fcd682903ad, 0x05424142450101423b8e5baa2963090161fc01eab9abf90eba3766aeb440bde6c1b01d8bc42f5301ec9e96d881fc7e683c596bfdfe0ccba427e7d22052818f401fa3f736559586]}} New head: {parentHash: 0x230fdd23a0b5167bca1ca76a49187ac2d6af502d5106952b51f43513d3c7e15c, number: 0x1622279, stateRoot: 0x2402f9ed5aac51091ce35bb637ce0cdde8b5c619605f595378cc8817451d30ad, extrinsicsRoot: 0xb276911ad92e1882e49e8c54754127f5097ab0647fdc103ae80ac6a937375bb1, digest: {logs: [0x0642414245b5010318000000119b301100000000aa58edbb34b52bcd3875c2ec4a4191f5cf54b4ac9e9eece6fe503a259a9172459a99c1bba25454b6ed0135c1813480280e2f5d0fd13317128f694fd079d84b015252bfb306f52d38b631aa8c306396195a3d52fc5adb92b99956256fcda10c0a, 0x04424545468403befc57c5c4d15a74507a0f5baf3e28fd7a264981db1779604d896ea8eb991b85, 0x05424142450101c09e18c1c5cf39abf1b47523c53d03b798fa1a9f0e0d7e76c306af5e9c06fe3d399d869f970209b17cdd9155a51056169e3e1ba394b2a23514a17607d301fc8b]}} ``` # Make a balance transfer Perhaps the most common operation done in any blockchain is transferring funds from one account to another. Here we will show you how to make a simple balance transfer using Polkadart. #### Make a transfer ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final wallet = await KeyPair.sr25519.fromUri("//Alice"); print('Alice\' wallet: ${wallet.address}'); // Get information necessary to build a proper extrinsic final runtimeVersion = await polkadot.rpc.state.getRuntimeVersion(); final currentBlockNumber = (await polkadot.query.system.number()) - 1; final currentBlockHash = await polkadot.query.system.blockHash(currentBlockNumber); final genesisHash = await polkadot.query.system.blockHash(0); final nonce = await polkadot.rpc.system.accountNextIndex(wallet.address); // Make the encoded call final multiAddress = $MultiAddress().id(wallet.publicKey.bytes); final transferCall = polkadot.tx.balances.transferKeepAlive(dest: multiAddress, value: BigInt.one); final encodedCall = transferCall.encode(); // Make the payload final payload = SigningPayload( method: encodedCall, specVersion: runtimeVersion.specVersion, transactionVersion: runtimeVersion.transactionVersion, genesisHash: encodeHex(genesisHash), blockHash: encodeHex(currentBlockHash), blockNumber: currentBlockNumber, eraPeriod: 64, nonce: nonce, tip: 0).encode(polkadot.registry); // Sign the payload and build the final extrinsic final signature = wallet.sign(payload); final extrinsic = ExtrinsicPayload( signer: wallet.bytes(), method: encodedCall, signature: signature, eraPeriod: 64, blockNumber: currentBlockNumber, nonce: nonce, tip: 0, ).encode(polkadot.registry, SignatureType.sr25519); // Send the extrinsic to the blockchain final author = AuthorApi(provider); await author.submitAndWatchExtrinsic(extrinsic, (data) { print(data); }); } // Output: // Alice' wallet: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY // ready: null // broadcast: [12D3KooWH6tanw7GDdvspfEpt2gEbn82Na84deJDnMNJptJCfAKU, 12D3KooWN8pzDyuAAZ126uR6WcwYThbMWSPnXf7cimKHCZVn34iB, 12D3KooWDUjbGEP1fExf7LDze8iScY6ycsB8B7K3Gfnb2wwffhxK, 12D3KooWHTuZXi5jfAztC68Wf9wbHk8g3pFs9uUVpw9tib13zQWf, 12D3KooWGGBCu1AwCLt5LWE63RTPorR3PjX7Q3cDZrRku59NPkB5, 12D3KooWB6iwHFUoLpSxp6jNCkExzukVJF1hKWNRBvZWtt34gVXh, 12D3KooWEtK675m8iU8Hpc1E5SLnBMPZgzZfdp4uUGDmyh7LEgR3, 12D3KooW9zVuJRTqsPJ5KoA7QSPzCxesYT8FhAKRDwdLdYXAkim3, 12D3KooWPgHhBcG2zD6uND3M6XWdd8MuXCU2Z6xjZs5typTVqUVd, 12D3KooWGQudnpbyypxqUmQdsmpGCfHDu9ip4oqeMM4M4pPxcSmJ, 12D3KooWSS1FFy4VgJmf5kioR5MotCS3LFNHWM3Z1GxQfmUcKqiy, 12D3KooWQZkoqjez2aJ8GsTf1K5vCY2PV3XxwVf9B3iuuYYxViEE, 12D3KooWHHyDiQXNvgtW2pxNsgGntpz6AuSni3Dgem6EYY7PeMZM, 12D3KooWLJ5DEEcifVAXnJZAyznxd6xWsEvwS6U43nD5JmUxQGPb, 12D3KooWADSbUQbTLHf4dzp8NtWK3DJFj3TSyWxv3v1wooCU77YC, 12D3KooWCmUArt3vWrmNTCtvXf7RCWWhgSgU5jUcqD1cgPiL7w1z, 12D3KooWPe66mj1D4T4KcwsJikeJScxSVNS2iae6oJHGnF99DTLc, 12D3KooWH3toosYujUH3jvZDrVsudJp9CEUc8ajyNGAUeMvRYqYd, 12D3KooWAuKeacpNPdFq7sLjXmgS23SyGu4Us7Z4fRiJoVK3aky2, 12D3KooWFUUj8yJ3sJzXjtPZAvhuK1bAhbn9JPa7KAYCoDN1MLmZ, 12D3KooWCx3GoZeH2MZ4eFmqeYa9RPppC4eZopW8GbEgyYcrrhQA, 12D3KooWPWHUu17HfaPuK7fbszNd6chFDzwHL3HCVw5miNfAKjLN, 12D3KooWPWRX8XUxV3pYeYb1fZpK2RSycTFhrVZ4Su65HxuYzRSh, 12D3KooWH57Fx8xR8FJhkDuvZDg2Jav3ZEJohjMtyLC1CGXVZ1vK, 12D3KooWLc4ieE6EGZ3B8yZ7JnaQqJKU7ocGXRVNufkE9Nh8E3XQ, 12D3KooWCRcoJqxeKVDTcKxqv4kdeUGkoJkWggZJyhrfhh4jAHY4, 12D3KooWAwWmTPHPeUruUrAm53KjUrAGVqdwSKPmRa7RC65yE7aL, 12D3KooWRuAURXtSTkbiz6tccf6QhABKuYmQw2w3wy16ZjhAQEsA, 12D3KooWAgv6KKySREnXRy6V136VM7orMtLTdXifcLkofUTeN76o, 12D3KooWLnf7gQkZVjDcKkoxfxAYuoJZcFukkV3zvkNMvYwvZLV4, 12D3KooWDjsafUnHj8SfCuG8pYBzNFMbq8GuTWQ9AT1SbSa4X3qy, 12D3KooWQj2P4jDanJtktEJnTFqbtv8rqo8s3iqaTi58yE5GMoPa, 12D3KooWJhMu1NSHC2xJgSi73GznghPXBc6oq2VZdUYvJJd8MLU7, 12D3KooWSz85vPmCcQ9fwaHpSrPyaAXRP2fB5phetz6txD444Jmn, 12D3KooWKz8dMRwWnw7UVZrrWegYBPBufBhpxBYSjk4JELyxKrTS, 12D3KooWLcFJj4mvUBD38FckCqafTYavJxZBeQknxVAvkhMFATiB, 12D3KooWNgzmWzP2cdDoinwsCva1vrXdN1bKHbnrzvkz66hQhmtc, 12D3KooWAbPXnoRSng2B5W9rXwz2A3tVrPMy8brnSvmDcyvnJs7X, 12D3KooWRmVgYuBa54Xo225zBAhfesdSPM8TL5JruNmYFUa5m9dP, 12D3KooW9xysa5Q6KuANonoMBp8AgYpCAcpyUk6Z2wxpEbN4ru2N] // inBlock: 0xc3c341788f41a28046b7e0cac16629a2205acf1259ebfb5b52da0f2ae9e09959 // finalized: 0xc3c341788f41a28046b7e0cac16629a2205acf1259ebfb5b52da0f2ae9e09959 ``` #### Using ExtrinsicBuilder (Fluent API) For a more streamlined experience, you can use the `ExtrinsicBuilder` which handles much of the boilerplate automatically: ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final wallet = await KeyPair.sr25519.fromUri("//Alice"); print('Alice\' wallet: ${wallet.address}'); // Create the transfer call final multiAddress = $MultiAddress().id(wallet.publicKey.bytes); final transferCall = polkadot.tx.balances.transferKeepAlive(dest: multiAddress, value: BigInt.one); // Build and sign the extrinsic using the fluent builder final extrinsic = await ExtrinsicBuilder(polkadot) .call(transferCall) .signer(wallet) .build(); // Send the extrinsic final author = AuthorApi(provider); await author.submitAndWatchExtrinsic(extrinsic, (data) { print(data); }); } ``` The `ExtrinsicBuilder` automatically fetches runtime version, block hash, nonce, and other required information, making it easier to construct valid extrinsics. # Read storage Reading the storage also called the chain state is a common operation in blockchain development. The storage can hold anything from balance of an account to the configuration of the chain. It is also possible to read the storage from a specific block, which is useful for historical data analysis. Here we are showing you how to retrieve the storage and decode it. #### Reading storage from the current block ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); polkadot.query.system.number().then((blockNumber) { print('Current block number: $blockNumber'); }); } // Output: Current block number: 23208989 ``` #### Reading storage from a specific block ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final block = decodeHex('0x43c65760a5e36e9c95b2c493b78e7b01a2f8be5b0b48d543168d9f2c6fc4eb10'); polkadot.query.system.number(at: block).then((blockNumber) { print('Current block number: $blockNumber'); }); } // Output: Current block number: 23208972 ``` # Using metadata hash Transactions are encoded and readed using the runtime metadata, for offline wallets this implies that they need to have access to a trustable source to provide its metadata. With the introduction of the frame\_metadata V15 and the [RFC-78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html){rel="nofollow"} It was introduced the merkleization of the metadata as an extra measure of security to prevent offline devices from signing transactions from fake metadatas. Polkadart through the `substrate_metadata` package provides a way to generate the merkleized metadata hash and use it to sign transactions. #### Make a transfer using metadata hash ```dart Future main(List arguments) async { final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io')); final polkadot = Polkadot(provider); final wallet = await KeyPair.sr25519.fromUri("//Alice"); print('Alice\' wallet: ${wallet.address}'); // Get information necessary to build a proper extrinsic final runtimeVersion = await polkadot.rpc.state.getRuntimeVersion(); final currentBlockNumber = (await polkadot.query.system.number()) - 1; final currentBlockHash = await polkadot.query.system.blockHash(currentBlockNumber); final genesisHash = await polkadot.query.system.blockHash(0); final nonce = await polkadot.rpc.system.accountNextIndex(wallet.address); // Make the encoded call final multiAddress = $MultiAddress().id(wallet.publicKey.bytes); final transferCall = polkadot.tx.balances.transferKeepAlive(dest: multiAddress, value: BigInt.one); final encodedCall = transferCall.encode(); // Get the MetadataHash final typedMetadata = await state.getTypedMetadata(); final merkleizer = MetadataMerkleizer.fromMetadata(typedMetadata.metadata, decimals: 10, tokenSymbol: 'DOT'); final metadataHash = hex.encode(merkleizer.digest()); // Make the payload final payload = SigningPayload( method: encodedCall, specVersion: runtimeVersion.specVersion, transactionVersion: runtimeVersion.transactionVersion, genesisHash: encodeHex(genesisHash), blockHash: encodeHex(currentBlockHash), blockNumber: currentBlockNumber, eraPeriod: 64, nonce: nonce, metadataHash: metadataHash, tip: 0).encode(polkadot.registry); // Sign the payload and build the final extrinsic final signature = wallet.sign(payload); final extrinsic = ExtrinsicPayload( signer: wallet.bytes(), method: encodedCall, signature: signature, eraPeriod: 64, blockNumber: currentBlockNumber, nonce: nonce, tip: 0, metadataHash: metadataHash, ).encode(polkadot.registry, SignatureType.sr25519); // Send the extrinsic to the blockchain final author = AuthorApi(provider); await author.submitAndWatchExtrinsic(extrinsic, (data) { print(data); }); } // Output: // Alice' wallet: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY // ready: null // broadcast: [12D3KooWH6tanw7GDdvspfEpt2gEbn82Na84deJDnMNJptJCfAKU, 12D3KooWN8pzDyuAAZ126uR6WcwYThbMWSPnXf7cimKHCZVn34iB, 12D3KooWDUjbGEP1fExf7LDze8iScY6ycsB8B7K3Gfnb2wwffhxK, 12D3KooWHTuZXi5jfAztC68Wf9wbHk8g3pFs9uUVpw9tib13zQWf, 12D3KooWGGBCu1AwCLt5LWE63RTPorR3PjX7Q3cDZrRku59NPkB5, 12D3KooWB6iwHFUoLpSxp6jNCkExzukVJF1hKWNRBvZWtt34gVXh, 12D3KooWEtK675m8iU8Hpc1E5SLnBMPZgzZfdp4uUGDmyh7LEgR3, 12D3KooW9zVuJRTqsPJ5KoA7QSPzCxesYT8FhAKRDwdLdYXAkim3, 12D3KooWPgHhBcG2zD6uND3M6XWdd8MuXCU2Z6xjZs5typTVqUVd, 12D3KooWGQudnpbyypxqUmQdsmpGCfHDu9ip4oqeMM4M4pPxcSmJ, 12D3KooWSS1FFy4VgJmf5kioR5MotCS3LFNHWM3Z1GxQfmUcKqiy, 12D3KooWQZkoqjez2aJ8GsTf1K5vCY2PV3XxwVf9B3iuuYYxViEE, 12D3KooWHHyDiQXNvgtW2pxNsgGntpz6AuSni3Dgem6EYY7PeMZM, 12D3KooWLJ5DEEcifVAXnJZAyznxd6xWsEvwS6U43nD5JmUxQGPb, 12D3KooWADSbUQbTLHf4dzp8NtWK3DJFj3TSyWxv3v1wooCU77YC, 12D3KooWCmUArt3vWrmNTCtvXf7RCWWhgSgU5jUcqD1cgPiL7w1z, 12D3KooWPe66mj1D4T4KcwsJikeJScxSVNS2iae6oJHGnF99DTLc, 12D3KooWH3toosYujUH3jvZDrVsudJp9CEUc8ajyNGAUeMvRYqYd, 12D3KooWAuKeacpNPdFq7sLjXmgS23SyGu4Us7Z4fRiJoVK3aky2, 12D3KooWFUUj8yJ3sJzXjtPZAvhuK1bAhbn9JPa7KAYCoDN1MLmZ, 12D3KooWCx3GoZeH2MZ4eFmqeYa9RPppC4eZopW8GbEgyYcrrhQA, 12D3KooWPWHUu17HfaPuK7fbszNd6chFDzwHL3HCVw5miNfAKjLN, 12D3KooWPWRX8XUxV3pYeYb1fZpK2RSycTFhrVZ4Su65HxuYzRSh, 12D3KooWH57Fx8xR8FJhkDuvZDg2Jav3ZEJohjMtyLC1CGXVZ1vK, 12D3KooWLc4ieE6EGZ3B8yZ7JnaQqJKU7ocGXRVNufkE9Nh8E3XQ, 12D3KooWCRcoJqxeKVDTcKxqv4kdeUGkoJkWggZJyhrfhh4jAHY4, 12D3KooWAwWmTPHPeUruUrAm53KjUrAGVqdwSKPmRa7RC65yE7aL, 12D3KooWRuAURXtSTkbiz6tccf6QhABKuYmQw2w3wy16ZjhAQEsA, 12D3KooWAgv6KKySREnXRy6V136VM7orMtLTdXifcLkofUTeN76o, 12D3KooWLnf7gQkZVjDcKkoxfxAYuoJZcFukkV3zvkNMvYwvZLV4, 12D3KooWDjsafUnHj8SfCuG8pYBzNFMbq8GuTWQ9AT1SbSa4X3qy, 12D3KooWQj2P4jDanJtktEJnTFqbtv8rqo8s3iqaTi58yE5GMoPa, 12D3KooWJhMu1NSHC2xJgSi73GznghPXBc6oq2VZdUYvJJd8MLU7, 12D3KooWSz85vPmCcQ9fwaHpSrPyaAXRP2fB5phetz6txD444Jmn, 12D3KooWKz8dMRwWnw7UVZrrWegYBPBufBhpxBYSjk4JELyxKrTS, 12D3KooWLcFJj4mvUBD38FckCqafTYavJxZBeQknxVAvkhMFATiB, 12D3KooWNgzmWzP2cdDoinwsCva1vrXdN1bKHbnrzvkz66hQhmtc, 12D3KooWAbPXnoRSng2B5W9rXwz2A3tVrPMy8brnSvmDcyvnJs7X, 12D3KooWRmVgYuBa54Xo225zBAhfesdSPM8TL5JruNmYFUa5m9dP, 12D3KooW9xysa5Q6KuANonoMBp8AgYpCAcpyUk6Z2wxpEbN4ru2N] // inBlock: 0xc3c341788f41a28046b7e0cac16629a2205acf1259ebfb5b52da0f2ae9e09959 // finalized: 0xc3c341788f41a28046b7e0cac16629a2205acf1259ebfb5b52da0f2ae9e09959 ``` You will notice that the example above is very similar to the one in [Make a balance transfer](https://polkadart.dev/guides/make-transfer). The difference here are the following: ```dart final typedMetadata = await state.getTypedMetadata(); final merkleizer = MetadataMerkleizer.fromMetadata(typedMetadata.metadata, decimals: 10, tokenSymbol: 'DOT'); final metadataHash = hex.encode(merkleizer.digest()); ``` 1. We use `state.getTypedMetadata()` to get the metadata v15 from the blockchain. 2. We use the `MetadataMerkleizer` to merkleize the metadata. 3. We use the `digest()` method to generate the metadata hash. ```dart // Make the payload final payload = SigningPayload( method: encodedCall, specVersion: runtimeVersion.specVersion, transactionVersion: runtimeVersion.transactionVersion, genesisHash: encodeHex(genesisHash), blockHash: encodeHex(currentBlockHash), blockNumber: currentBlockNumber, eraPeriod: 64, nonce: nonce, metadataHash: metadataHash, tip: 0).encode(polkadot.registry); // Sign the payload and build the final extrinsic final signature = wallet.sign(payload); final extrinsic = ExtrinsicPayload( signer: wallet.bytes(), method: encodedCall, signature: signature, eraPeriod: 64, blockNumber: currentBlockNumber, nonce: nonce, tip: 0, metadataHash: metadataHash, ).encode(polkadot.registry, SignatureType.sr25519); ``` Now, notice that we are providing the `metadataHash` in the `SigningPayload` and `ExtrinsicPayload` objects. This will ensure that your transaction was signed with the correct metadata and if not it will be rejected by the blockchain. # Community & Support ## Get Support ::card-grid :::card --- icon: i-simple-icons-github target: _blank title: GitHub Discussions to: https://github.com/leonardocustodio/polkadart/discussions --- Ask questions and share ideas with the community ::: :::card --- icon: i-simple-icons-matrix target: _blank title: Matrix Chat to: https://matrix.to/#/#polkadart:matrix.org --- Real-time chat with developers and community members ::: :::card --- icon: i-heroicons-question-mark-circle target: _blank title: Stack Exchange to: https://substrate.stackexchange.com/ --- Q&A for Substrate and Polkadot development ::: :::card --- icon: i-heroicons-bug-ant target: _blank title: Report Issues to: https://github.com/leonardocustodio/polkadart/issues --- Report bugs or request new features ::: :: ## Built with Polkadart Projects using Polkadart in production: ::callout{type="info"} **Encointer** \- Money by the people, for the people :: ::callout{type="tip"} **Built something with Polkadart?** [Submit a PR](https://github.com/leonardocustodio/polkadart/pulls){rel="nofollow"} to add your project here! :: ## Contributing We welcome contributions from the community! Here's how you can help: ::card-grid :::card --- icon: i-heroicons-code-bracket --- #title Code Contributions #description Fix bugs, add features, or improve performance ::: :::card --- icon: i-heroicons-document-text --- #title Documentation #description Improve docs, add examples, or translate content ::: :::card --- icon: i-heroicons-beaker --- #title Testing #description Write tests, report bugs, or verify fixes ::: :::card --- icon: i-heroicons-chat-bubble-left-right --- #title Community Support #description Answer questions, share knowledge, or create tutorials ::: :: ### Getting Started with Contributing ::steps :::step ## Read Contributing Guide Read our [Contributing Guide](https://github.com/leonardocustodio/polkadart/blob/main/CONTRIBUTING.md){rel="nofollow"} ::: :::step ## Find Tasks Check [open issues](https://github.com/leonardocustodio/polkadart/issues){rel="nofollow"} for tasks ::: :::step ## Join Discussion Join our [Matrix chat](https://matrix.to/#/#polkadart\:matrix.org){rel="nofollow"} to discuss ideas ::: :::step ## Fork Repository Fork the repository and make your changes ::: :::step ## Submit PR Submit a pull request ::: :: ## Resources ### Learning Resources ::field-group :::field{icon="i-simple-icons-polkadot" label="Polkadot Wiki"} Official Polkadot documentation [Visit β†’](https://wiki.polkadot.network/){rel="nofollow"} ::: :::field{icon="i-simple-icons-substrate" label="Substrate Docs"} Learn about Substrate framework [Visit β†’](https://docs.substrate.io/){rel="nofollow"} ::: :::field{icon="i-heroicons-document-text" label="ink! Documentation"} Smart contract development [Visit β†’](https://use.ink/){rel="nofollow"} ::: :: ### Developer Tools ::field-group :::field{icon="i-heroicons-globe-alt" label="Polkadot.js Apps"} Blockchain explorer and wallet [Visit β†’](https://polkadot.js.org/apps/){rel="nofollow"} ::: :::field{icon="i-heroicons-magnifying-glass" label="Subscan"} Multi-chain explorer [Visit β†’](https://www.subscan.io/){rel="nofollow"} ::: :::field{icon="i-heroicons-cube" label="Contracts UI"} Smart contract interface [Visit β†’](https://contracts-ui.substrate.io/){rel="nofollow"} ::: :: ## Stay Connected ::card-grid :::card --- icon: i-simple-icons-github target: _blank title: GitHub to: https://github.com/leonardocustodio/polkadart --- Star and watch for updates ::: :::card --- icon: i-simple-icons-x target: _blank title: Twitter/X to: https://twitter.com/polkadart_io --- Follow for news and updates ::: :::card --- icon: i-simple-icons-matrix target: _blank title: Matrix to: https://matrix.to/#/#polkadart:matrix.org --- Join the chat community ::: :::card{icon="i-heroicons-envelope" title="Newsletter"} Subscribe for monthly updates (coming soon) ::: :: ## Acknowledgments Polkadart is made possible by: - **Web3 Foundation** - Grant support and guidance - **Contributors** - Amazing developers from around the world - **Community** - Users who provide feedback and support - **Polkadot Ecosystem** - Inspiration and collaboration