# Moonbeam Developer Documentation (LLMS Format) This file contains documentation for Moonbeam (https://moonbeam.network/). Moonbeam is a smart contract platform that makes it easy to build natively interoperable applications on Polkadot and Ethereum. It is intended for use with large language models (LLMs) to support developers working with Moonbeam. The content includes selected pages from the official docs, organized by section. This file includes documentation related to the product: Precompiles ## AI Prompt Template You are an AI developer assistant for Moonbeam (https://moonbeam.network/). Your task is to assist developers in understanding and using the product described in this file. - Provide accurate answers based on the included documentation. - Do not assume undocumented features, behaviors, or APIs. - If unsure, respond with “Not specified in the documentation. ## List of doc pages: Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/canonical-contracts.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/account/identity.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/account/proxy.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/features/governance/collective.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/features/governance/conviction-voting.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/features/governance/preimage.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/features/governance/referenda.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/features/randomness.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/features/staking.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/interoperability/gmp.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/utility/eth-mainnet.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/utility/non-specific.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/utility/registry.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/utility/relay-data-verifier.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/ux/batch.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/ux/call-permit.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/ethereum/precompiles/ux/erc20.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/interoperability/xcm/xc20/send-xc20s/eth-api.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/builders/interoperability/xcm/xc20/send-xc20s/xtokens-precompile.md [type: builders] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/tutorials/eth-api/batch-approve-swap.md [type: tutorials] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/tutorials/eth-api/call-permit-gasless-txs.md [type: tutorials] Doc-Page: https://raw.githubusercontent.com/moonbeam-foundation/moonbeam-docs/refs/heads/master/tutorials/eth-api/randomness-lottery.md [type: tutorials] ## Full content for each doc page Doc-Content: https://docs.moonbeam.network/builders/ethereum/canonical-contracts/ --- BEGIN CONTENT --- --- title: Canonical Contract Addresses on Moonbeam description: Overview of the canonical contracts available on Moonbeam, Moonriver, & Moonbase Alpha, including common-good contracts and precompiles. keywords: canonical, ethereum, moonbeam, precompiled, contracts categories: Reference, Precompiles, Ethereum Toolkit --- # Canonical Contracts ## Common-good Contracts {: #common-goods-contracts } The following contracts addresses have been established: === "Moonbeam" | Contract | Address | |:------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [WGLMR](https://moonbeam.moonscan.io/address/0xAcc15dC74880C9944775448304B263D191c6077F#code){target=\_blank} | 0xAcc15dC74880C9944775448304B263D191c6077F | | [Multicall](https://moonbeam.moonscan.io/address/0x83e3b61886770de2F64AAcaD2724ED4f08F7f36B#code){target=\_blank} | 0x83e3b61886770de2F64AAcaD2724ED4f08F7f36B | | [Multicall2](https://moonbeam.moonscan.io/address/0x6477204E12A7236b9619385ea453F370aD897bb2#code){target=\_blank} | 0x6477204E12A7236b9619385ea453F370aD897bb2 | | [Multicall3](https://moonbeam.moonscan.io/address/0xca11bde05977b3631167028862be2a173976ca11#code){target=\_blank} | 0xcA11bde05977b3631167028862bE2a173976CA11 | | [Multisig Factory](https://moonbeam.moonscan.io/address/0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2#code){target=\_blank} | 0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2 | | [EIP-1820](https://eips.ethereum.org/EIPS/eip-1820){target=\_blank} | 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 | === "Moonriver" | Contract | Address | |:-------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [WMOVR](https://moonriver.moonscan.io/token/0x98878b06940ae243284ca214f92bb71a2b032b8a#code){target=\_blank} | 0x98878B06940aE243284CA214f92Bb71a2b032B8A | | [Multicall](https://moonriver.moonscan.io/address/0x30f283Cc0284482e9c29dFB143bd483B5C19954b#code){target=\_blank}* | 0x30f283Cc0284482e9c29dFB143bd483B5C19954b | | [Multicall2](https://moonriver.moonscan.io/address/0xaef00a0cf402d9dedd54092d9ca179be6f9e5ce3#code){target=\_blank} | 0xaef00a0cf402d9dedd54092d9ca179be6f9e5ce3 | | [Multicall3](https://moonriver.moonscan.io/address/0xca11bde05977b3631167028862be2a173976ca11#code){target=\_blank} | 0xcA11bde05977b3631167028862bE2a173976CA11 | | [Multisig Factory](https://moonriver.moonscan.io/address/0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2#code){target=\_blank} | 0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2 | | [EIP-1820](https://eips.ethereum.org/EIPS/eip-1820){target=\_blank} | 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 | _*Deployed by SushiSwap_ === "Moonbase Alpha" | Contract | Address | |:------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [WDEV](https://moonbase.moonscan.io/address/0xD909178CC99d318e4D46e7E66a972955859670E1#code){target=\_blank} | 0xD909178CC99d318e4D46e7E66a972955859670E1 | | [Multicall](https://moonbase.moonscan.io/address/0x4E2cfca20580747AdBA58cd677A998f8B261Fc21#code){target=\_blank}* | 0x4E2cfca20580747AdBA58cd677A998f8B261Fc21 | | [Multicall2](https://moonbase.moonscan.io/address/0x37084d0158C68128d6Bc3E5db537Be996f7B6979#code){target=\_blank} | 0x37084d0158C68128d6Bc3E5db537Be996f7B6979 | | [Multicall3](https://moonbase.moonscan.io/address/0xca11bde05977b3631167028862be2a173976ca11#code){target=\_blank} | 0xcA11bde05977b3631167028862bE2a173976CA11 | | [Multisig Factory](https://moonbase.moonscan.io/address/0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2#code){target=\_blank} | 0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2 | | [EIP-1820](https://eips.ethereum.org/EIPS/eip-1820){target=\_blank} | 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 | _*Deployed in the [UniswapV2 Demo Repo](https://github.com/papermoonio/moonbeam-uniswap/tree/main/uniswap-contracts-moonbeam){target=\_blank}_ ## Precompiled Contracts {: #precompiled-contracts } There are a set of precompiled contracts included on Moonbeam, Moonriver, and Moonbase Alpha that are categorized by address and based on the origin network. If you were to convert the precompiled addresses to decimal format, and break them into categories by numeric value, the categories are as follows: - **0-1023** - [Ethereum MainNet precompiles](#ethereum-mainnet-precompiles) - **1024-2047** - precompiles that are [not in Ethereum and not Moonbeam specific](#non-moonbeam-specific-nor-ethereum-precomiles) - **2048-4095** - [Moonbeam specific precompiles](#moonbeam-specific-precompiles) ### Ethereum MainNet Precompiles {: #ethereum-mainnet-precompiles } === "Moonbeam" | Contract | Address | |:---------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [ECRECOVER](/builders/ethereum/precompiles/utility/eth-mainnet/#verify-signatures-with-ecrecover){target=\_blank} | 0x0000000000000000000000000000000000000001 | | [SHA256](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-sha256){target=\_blank} | 0x0000000000000000000000000000000000000002 | | [RIPEMD160](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-ripemd-160){target=\_blank} | 0x0000000000000000000000000000000000000003 | | [Identity](/builders/ethereum/precompiles/utility/eth-mainnet/#the-identity-function){target=\_blank} | 0x0000000000000000000000000000000000000004 | | [Modular Exponentiation](/builders/ethereum/precompiles/utility/eth-mainnet/#modular-exponentiation){target=\_blank} | 0x0000000000000000000000000000000000000005 | | [BN128Add](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128add){target=\_blank} | 0x0000000000000000000000000000000000000006 | | [BN128Mul](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128mul){target=\_blank} | 0x0000000000000000000000000000000000000007 | | [BN128Pairing](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128pairing){target=\_blank} | 0x0000000000000000000000000000000000000008 | | [Blake2](https://polkadot-evm.github.io/frontier/rustdocs/pallet_evm_precompile_blake2/struct.Blake2F.html){target=\_blank} | 0x0000000000000000000000000000000000000009 | | [P256Verify](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md){target=\_blank} | 0x0000000000000000000000000000000000000100 | === "Moonriver" | Contract | Address | |:---------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [ECRECOVER](/builders/ethereum/precompiles/utility/eth-mainnet/#verify-signatures-with-ecrecover){target=\_blank} | 0x0000000000000000000000000000000000000001 | | [SHA256](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-sha256){target=\_blank} | 0x0000000000000000000000000000000000000002 | | [RIPEMD160](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-ripemd-160){target=\_blank} | 0x0000000000000000000000000000000000000003 | | [Identity](/builders/ethereum/precompiles/utility/eth-mainnet/#the-identity-function){target=\_blank} | 0x0000000000000000000000000000000000000004 | | [Modular Exponentiation](/builders/ethereum/precompiles/utility/eth-mainnet/#modular-exponentiation){target=\_blank} | 0x0000000000000000000000000000000000000005 | | [BN128Add](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128add){target=\_blank} | 0x0000000000000000000000000000000000000006 | | [BN128Mul](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128mul){target=\_blank} | 0x0000000000000000000000000000000000000007 | | [BN128Pairing](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128pairing){target=\_blank} | 0x0000000000000000000000000000000000000008 | | [Blake2](https://polkadot-evm.github.io/frontier/rustdocs/pallet_evm_precompile_blake2/struct.Blake2F.html){target=\_blank} | 0x0000000000000000000000000000000000000009 | | [P256Verify](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md){target=\_blank} | 0x0000000000000000000000000000000000000100 | === "Moonbase Alpha" | Contract | Address | |:---------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [ECRECOVER](/builders/ethereum/precompiles/utility/eth-mainnet/#verify-signatures-with-ecrecover){target=\_blank} | 0x0000000000000000000000000000000000000001 | | [SHA256](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-sha256){target=\_blank} | 0x0000000000000000000000000000000000000002 | | [RIPEMD160](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-ripemd-160){target=\_blank} | 0x0000000000000000000000000000000000000003 | | [Identity](/builders/ethereum/precompiles/utility/eth-mainnet/#the-identity-function){target=\_blank} | 0x0000000000000000000000000000000000000004 | | [Modular Exponentiation](/builders/ethereum/precompiles/utility/eth-mainnet/#modular-exponentiation){target=\_blank} | 0x0000000000000000000000000000000000000005 | | [BN128Add](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128add){target=\_blank} | 0x0000000000000000000000000000000000000006 | | [BN128Mul](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128mul){target=\_blank} | 0x0000000000000000000000000000000000000007 | | [BN128Pairing](/builders/ethereum/precompiles/utility/eth-mainnet/#bn128pairing){target=\_blank} | 0x0000000000000000000000000000000000000008 | | [Blake2](https://polkadot-evm.github.io/frontier/rustdocs/pallet_evm_precompile_blake2/struct.Blake2F.html){target=\_blank} | 0x0000000000000000000000000000000000000009 | | [P256Verify](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md){target=\_blank} | 0x0000000000000000000000000000000000000100 | ### Non-Moonbeam Specific nor Ethereum Precompiles {: #non-moonbeam-specific-nor-ethereum-precompiles } === "Moonbeam" | Contract | Address | |:--------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [SHA3FIPS256](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-sha3fips256){target=\_blank} | 0x0000000000000000000000000000000000000400 | | Dispatch [Removed] | 0x0000000000000000000000000000000000000401 | | [ECRecoverPublicKey](https://polkadot-evm.github.io/frontier/rustdocs/pallet_evm_precompile_simple/struct.ECRecoverPublicKey.html){target=\_blank} | 0x0000000000000000000000000000000000000402 | === "Moonriver" | Contract | Address | |:--------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [SHA3FIPS256](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-sha3fips256){target=\_blank} | 0x0000000000000000000000000000000000000400 | | Dispatch [Removed] | 0x0000000000000000000000000000000000000401 | | [ECRecoverPublicKey](https://polkadot-evm.github.io/frontier/rustdocs/pallet_evm_precompile_simple/struct.ECRecoverPublicKey.html){target=\_blank} | 0x0000000000000000000000000000000000000402 | === "Moonbase Alpha" | Contract | Address | |:-------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------:| | [SHA3FIPS256](/builders/ethereum/precompiles/utility/eth-mainnet/#hashing-with-sha3fips256){target=\_blank} | 0x0000000000000000000000000000000000000400 | | Dispatch [Removed] | 0x0000000000000000000000000000000000000401 | | [ECRecoverPublicKey](https://polkadot-evm.github.io/frontier/rustdocs/pallet_evm_precompile_simple/struct.ECRecoverPublicKey.html){target=\_blank} | 0x0000000000000000000000000000000000000402 | ### Moonbeam-Specific Precompiles {: #moonbeam-specific-precompiles } === "Moonbeam" | Contract | Address | |:-------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------:| | [Parachain Staking](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/parachain-staking/StakingInterface.sol){target=\_blank} | {{networks.moonbeam.precompiles.staking}} | | [Crowdloan Rewards](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/crowdloan-rewards/CrowdloanInterface.sol){target=\_blank} | {{networks.moonbeam.precompiles.crowdloan}} | | [ERC-20 Interface](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/balances-erc20/ERC20.sol){target=\_blank} | {{networks.moonbeam.precompiles.erc20}} | | Democracy [Removed] | {{networks.moonbeam.precompiles.democracy}} | | [X-Tokens](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xtokens/Xtokens.sol){target=\_blank} | {{networks.moonbeam.precompiles.xtokens}} | | [Relay Encoder](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/relay-encoder/RelayEncoder.sol){target=\_blank} | {{networks.moonbeam.precompiles.relay_encoder}} | | [XCM Transactor V1](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v1/XcmTransactorV1.sol){target=\_blank} | {{networks.moonbeam.precompiles.xcm_transactor_v1}} | | [Author Mapping](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/author-mapping/AuthorMappingInterface.sol){target=\_blank} | {{networks.moonbeam.precompiles.author_mapping}} | | [Batch](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/batch/Batch.sol){target=\_blank} | {{networks.moonbeam.precompiles.batch}} | | [Randomness](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/randomness/Randomness.sol){target=\_blank} | {{networks.moonbeam.precompiles.randomness}} | | [Call Permit](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/call-permit/CallPermit.sol){target=\_blank} | {{networks.moonbeam.precompiles.call_permit}} | | [Proxy](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/proxy/Proxy.sol){target=\_blank} | {{networks.moonbeam.precompiles.proxy}} | | [XCM Utilities](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-utils/XcmUtils.sol){target=\_blank} | {{networks.moonbeam.precompiles.xcm_utils}} | | [XCM Transactor V2](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v2/XcmTransactorV2.sol){target=\_blank} | {{networks.moonbeam.precompiles.xcm_transactor_v2}} | | [Council Collective [Removed]](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbeam.precompiles.collective_council}} | | [Technical Committee Collective [Removed]](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbeam.precompiles.collective_tech_committee}} | | [Treasury Council Collective](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbeam.precompiles.collective_treasury}} | | [Referenda](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/referenda/Referenda.sol){target=\_blank} | {{networks.moonbeam.precompiles.referenda}} | | [Conviction Voting](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/conviction-voting/ConvictionVoting.sol){target=\_blank} | {{networks.moonbeam.precompiles.conviction_voting}} | | [Preimage](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/preimage/Preimage.sol){target=\_blank} | {{networks.moonbeam.precompiles.preimage}} | | [OpenGov Tech Committee](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbeam.precompiles.collective_opengov_tech_committee}} | | [Precompile Registry](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/precompile-registry/PrecompileRegistry.sol){target=\_blank} | {{networks.moonbeam.precompiles.registry}} | | [GMP](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/gmp/Gmp.sol){target=\_blank} | {{networks.moonbeam.precompiles.gmp}} | | [XCM Transactor V3](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v3/XcmTransactorV3.sol){target=\_blank} | {{networks.moonbeam.precompiles.xcm_transactor_v3}} | | [Identity](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/identity/Identity.sol){target=\_blank} | {{networks.moonbeam.precompiles.identity}} | | [XCM Interface](https://github.com/Moonsong-Labs/moonkit/blob/main/precompiles/pallet-xcm/XcmInterface.sol){target=\_blank} | {{networks.moonbeam.precompiles.xcm_interface}} | === "Moonriver" | Contract | Address | |:-------------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------:| | [Parachain Staking](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/parachain-staking/StakingInterface.sol){target=\_blank} | {{networks.moonriver.precompiles.staking}} | | [Crowdloan Rewards](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/crowdloan-rewards/CrowdloanInterface.sol){target=\_blank} | {{networks.moonriver.precompiles.crowdloan}} | | [ERC-20 Interface](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/balances-erc20/ERC20.sol){target=\_blank} | {{networks.moonriver.precompiles.erc20}} | | Democracy [Removed] | {{networks.moonriver.precompiles.democracy}} | | [X-Tokens](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xtokens/Xtokens.sol){target=\_blank} | {{networks.moonriver.precompiles.xtokens}} | | [Relay Encoder](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/relay-encoder/RelayEncoder.sol){target=\_blank} | {{networks.moonriver.precompiles.relay_encoder}} | | [XCM Transactor V1](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v1/XcmTransactorV1.sol){target=\_blank} | {{networks.moonriver.precompiles.xcm_transactor_v1}} | | [Author Mapping](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/author-mapping/AuthorMappingInterface.sol){target=\_blank} | {{networks.moonriver.precompiles.author_mapping}} | | [Batch](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/batch/Batch.sol){target=\_blank} | {{networks.moonriver.precompiles.batch}} | | [Randomness](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/randomness/Randomness.sol){target=\_blank} | {{networks.moonriver.precompiles.randomness}} | | [Call Permit](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/call-permit/CallPermit.sol){target=\_blank} | {{networks.moonriver.precompiles.call_permit}} | | [Proxy](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/proxy/Proxy.sol){target=\_blank} | {{networks.moonriver.precompiles.proxy}} | | [XCM Utilities](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-utils/XcmUtils.sol){target=\_blank} | {{networks.moonriver.precompiles.xcm_utils}} | | [XCM Transactor V2](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v2/XcmTransactorV2.sol){target=\_blank} | {{networks.moonriver.precompiles.xcm_transactor_v2}} | | [Council Collective [Removed]](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonriver.precompiles.collective_council}} | | [Technical Committee Collective [Removed]](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonriver.precompiles.collective_tech_committee}} | | [Treasury Council Collective](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonriver.precompiles.collective_treasury}} | | [Referenda](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/referenda/Referenda.sol){target=\_blank} | {{networks.moonriver.precompiles.referenda}} | | [Conviction Voting](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/conviction-voting/ConvictionVoting.sol){target=\_blank} | {{networks.moonriver.precompiles.conviction_voting}} | | [Preimage](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/preimage/Preimage.sol){target=\_blank} | {{networks.moonriver.precompiles.preimage}} | | [OpenGov Tech Committee](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonriver.precompiles.collective_opengov_tech_committee}} | | [Precompile Registry](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/precompile-registry/PrecompileRegistry.sol){target=\_blank} | {{networks.moonriver.precompiles.registry}} | | [GMP](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/gmp/Gmp.sol){target=\_blank} | {{networks.moonriver.precompiles.gmp}} | | [XCM Transactor V3](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v3/XcmTransactorV3.sol){target=\_blank} | {{networks.moonriver.precompiles.xcm_transactor_v3}} | | [Identity](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/identity/Identity.sol){target=\_blank} | {{networks.moonriver.precompiles.identity}} | | [XCM Interface](https://github.com/Moonsong-Labs/moonkit/blob/main/precompiles/pallet-xcm/XcmInterface.sol){target=\_blank} | {{networks.moonriver.precompiles.xcm_interface}} | === "Moonbase Alpha" | Contract | Address | |:-------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------:| | [Parachain Staking](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/parachain-staking/StakingInterface.sol){target=\_blank} | {{networks.moonbase.precompiles.staking}} | | [Crowdloan Rewards](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/crowdloan-rewards/CrowdloanInterface.sol){target=\_blank} | {{networks.moonbase.precompiles.crowdloan}} | | [ERC-20 Interface](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/balances-erc20/ERC20.sol){target=\_blank} | {{networks.moonbase.precompiles.erc20}} | | Democracy [Removed] | {{networks.moonbase.precompiles.democracy}} | | [X-Tokens](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xtokens/Xtokens.sol){target=\_blank} | {{networks.moonbase.precompiles.xtokens}} | | [Relay Encoder](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/relay-encoder/RelayEncoder.sol){target=\_blank} | {{networks.moonbase.precompiles.relay_encoder}} | | [XCM Transactor V1](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v1/XcmTransactorV1.sol){target=\_blank} | {{networks.moonbase.precompiles.xcm_transactor_v1}} | | [Author Mapping](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/author-mapping/AuthorMappingInterface.sol){target=\_blank} | {{networks.moonbase.precompiles.author_mapping}} | | [Batch](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/batch/Batch.sol){target=\_blank} | {{networks.moonbase.precompiles.batch}} | | [Randomness](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/randomness/Randomness.sol){target=\_blank} | {{networks.moonbase.precompiles.randomness}} | | [Call Permit](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/call-permit/CallPermit.sol){target=\_blank} | {{networks.moonbase.precompiles.call_permit}} | | [Proxy](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/proxy/Proxy.sol){target=\_blank} | {{networks.moonbase.precompiles.proxy}} | | [XCM Utilities](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-utils/XcmUtils.sol){target=\_blank} | {{networks.moonbase.precompiles.xcm_utils}} | | [XCM Transactor V2](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v2/XcmTransactorV2.sol){target=\_blank} | {{networks.moonbase.precompiles.xcm_transactor_v2}} | | [Council Collective [Removed]](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbase.precompiles.collective_council}} | | [Technical Committee Collective [Removed]](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbase.precompiles.collective_tech_committee}} | | [Treasury Council Collective](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbase.precompiles.collective_treasury}} | | [Referenda](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/referenda/Referenda.sol){target=\_blank} | {{networks.moonbase.precompiles.referenda}} | | [Conviction Voting](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/conviction-voting/ConvictionVoting.sol){target=\_blank} | {{networks.moonbase.precompiles.conviction_voting}} | | [Preimage](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/preimage/Preimage.sol){target=\_blank} | {{networks.moonbase.precompiles.preimage}} | | [OpenGov Tech Committee](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} | {{networks.moonbase.precompiles.collective_opengov_tech_committee}} | | [Precompile Registry](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/precompile-registry/PrecompileRegistry.sol){target=\_blank} | {{networks.moonbase.precompiles.registry}} | | [GMP](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/gmp/Gmp.sol){target=\_blank} | {{networks.moonbase.precompiles.gmp}} | | [XCM Transactor V3](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xcm-transactor/src/v3/XcmTransactorV3.sol){target=\_blank} | {{networks.moonbase.precompiles.xcm_transactor_v3}} | | [Identity](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/identity/Identity.sol){target=\_blank} | {{networks.moonbase.precompiles.identity}} | | [XCM Interface](https://github.com/Moonsong-Labs/moonkit/blob/main/precompiles/pallet-xcm/XcmInterface.sol){target=\_blank} | {{networks.moonbase.precompiles.xcm_interface}} | --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/account/identity/ --- BEGIN CONTENT --- --- title: Identity Precompile description: Learn all you need to know about the Identity Precompile, such as its address, Solidity interface, and how to interact with it using popular Ethereum libraries. categories: Precompiles, Ethereum Toolkit --- # Identity Precompile on Moonbeam ## Introduction {: #introduction } The Identity Precompile is a Solidity interface that allows you to create, manage, and retrieve information on on-chain identities. Identities are attached to accounts and include personal information, such as your legal name, display name, website, Twitter handle, Riot (now known as Element) name, and more. You can also take advantage of custom fields to include any other relevant information. The Identity Precompile interacts directly with Substrate's [Identity Pallet](/builders/substrate/interfaces/account/identity/){target=\_blank} to provide the functionality needed to create and manage identities. This pallet is coded in Rust and is normally not accessible from the Ethereum side of Moonbeam. However, the Identity Precompile allows you to access this functionality directly from the Solidity interface. The Identity Precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.identity }} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.identity }} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.identity }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Identity Precompile Solidity Interface {: #the-solidity-interface } [`Identity.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/identity/Identity.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's methods. ??? code "Identity.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The Identity contract's address. address constant IDENTITY_ADDRESS = 0x0000000000000000000000000000000000000818; /// @dev The Identity contract's instance. Identity constant IDENTITY_CONTRACT = Identity(IDENTITY_ADDRESS); /// @author The Moonbeam Team /// @title Pallet Identity Interface /// @title The interface through which solidity contracts will interact with the Identity pallet /// @custom:address 0x0000000000000000000000000000000000000818 interface Identity { /// @dev Associated raw data. struct Data { /// Is `true` if it represents data, else the absence of data is represented by `false`. bool hasData; /// The contained value. bytes value; } /// @dev The super-identity of an alternative "sub" identity. struct SuperOf { /// Is `true` if the struct is valid, `false` otherwise. bool isValid; /// The super account. address account; /// The associated data. Data data; } /// @dev Alternative "sub" identities of an account. struct SubsOf { /// The deposit against this identity. uint256 deposit; /// The sub accounts address[] accounts; } /// @dev Registrar judgements are limited to attestations on these fields. struct IdentityFields { /// Set to `true` if the display field is supported, `false` otherwise. bool display; /// Set to `true` if the legal field is supported, `false` otherwise. bool legal; /// Set to `true` if the web field is supported, `false` otherwise. bool web; /// Set to `true` if the riot field is supported, `false` otherwise. bool riot; /// Set to `true` if the email field is supported, `false` otherwise. bool email; /// Set to `true` if the PGP Fingerprint field is supported, `false` otherwise. bool pgpFingerprint; /// Set to `true` if the image field is supported, `false` otherwise. bool image; /// Set to `true` if the twitter field is supported, `false` otherwise. bool twitter; } /// @dev Registrar info. struct Registrar { /// Is `true` if the struct is valid, `false` otherwise. bool isValid; /// The registrar's index. uint32 index; /// The account address. address account; /// Amount required to be given to the registrar for them to provide judgement. uint256 fee; /// Relevant fields for this registrar. IdentityFields fields; } /// @dev Represents an additional field in identity info. struct Additional { /// The assciated key. Data key; /// The assciated value. Data value; } /// @dev The identity information set for an account. struct IdentityInfo { /// Represents the additional fields for the identity. Additional[] additional; /// Represents the display info for the identity. Data display; /// Represents the legal info for the identity. Data legal; /// Represents the web info for the identity. Data web; /// Represents the riot info for the identity. Data riot; /// Represents the email info for the identity. Data email; /// Set to `true` if `pgpFingerprint` is set, `false` otherwise. bool hasPgpFingerprint; /// Represents a 20-byte the PGP fingerprint info for the identity. bytes pgpFingerprint; /// Represents the image info for the identity. Data image; /// Represents the twitter info for the identity. Data twitter; } /// @dev Judgement provided by a registrar. struct Judgement { /// The default value; no opinion is held. bool isUnknown; /// No judgement is yet in place, but a deposit is reserved as payment for providing one. bool isFeePaid; /// The deposit reserved for providing a judgement. uint256 feePaidDeposit; /// The data appears to be reasonably acceptable in terms of its accuracy. bool isReasonable; /// The target is known directly by the registrar and the registrar can fully attest to it. bool isKnownGood; /// The data was once good but is currently out of date. bool isOutOfDate; /// The data is imprecise or of sufficiently low-quality to be problematic. bool isLowQuality; /// The data is erroneous. This may be indicative of malicious intent. bool isErroneous; } /// @dev Judgement item provided by a registrar. struct JudgementInfo { /// The registrar's index that provided this judgement. uint32 registrarIndex; /// The registrar's provided judgement. Judgement judgement; } /// @dev Registrar info. struct Registration { /// Is `true` if the struct is valid, `false` otherwise. bool isValid; /// The judgments provided on this identity. JudgementInfo[] judgements; /// Amount required to be given to the registrar for them to provide judgement. uint256 deposit; /// The associated identity info. IdentityInfo info; } /// @dev Alternative "sub" identity of an account. struct SubAccount { /// The account address. address account; /// The associated data. Data data; } /// @dev Retrieve identity information for an account. /// @custom:selector f0eb5e54 /// @param who The requested account function identity(address who) external view returns (Registration memory); /// @dev Retrieve super account for an account. /// @custom:selector c18110d6 /// @param who The requested account function superOf(address who) external view returns (SuperOf memory); /// @dev Retrieve sub accounts for an account. /// @custom:selector 3f08986b /// @param who The requested account function subsOf(address who) external view returns (SubsOf memory); /// @dev Retrieve the registrars. /// @custom:selector e88e512e function registrars() external view returns (Registrar[] memory); /// @dev Set identity info for the caller. /// @custom:selector 7e08b4cb /// @param info The identity info function setIdentity(IdentityInfo memory info) external; /// @dev Set sub accounts for the caller. /// @custom:selector 5a5a3591 /// @param subs The sub accounts function setSubs(SubAccount[] memory subs) external; /// @dev Clears identity of the caller. /// @custom:selector 7a6a10c7 function clearIdentity() external; /// @dev Requests registrar judgement on caller's identity. /// @custom:selector d523ceb9 /// @param regIndex The registrar's index /// @param maxFee The maximum fee the caller is willing to pay function requestJudgement(uint32 regIndex, uint256 maxFee) external; /// @dev Cancels the caller's request for judgement from a registrar. /// @custom:selector c79934a5 /// @param regIndex The registrar's index function cancelRequest(uint32 regIndex) external; /// @dev Sets the registrar's fee for providing a judgement. Caller must be the account at the index. /// @custom:selector a541b37d /// @param regIndex The registrar's index /// @param fee The fee the registrar will charge function setFee(uint32 regIndex, uint256 fee) external; /// @dev Sets the registrar's account. Caller must be the account at the index. /// @custom:selector 889bc198 /// @param regIndex The registrar's index /// @param newAccount The new account to set function setAccountId(uint32 regIndex, address newAccount) external; /// @dev Sets the registrar's identity fields. Caller must be the account at the index. /// @custom:selector 05297450 /// @param regIndex The registrar's index /// @param fields The identity fields function setFields(uint32 regIndex, IdentityFields memory fields) external; /// @dev Provides judgement on an accounts identity. /// @custom:selector cd7663a4 /// @param regIndex The registrar's index /// @param target The target account to provide judgment for /// @param judgement The judgement to provide /// @param identity The hash of the identity info function provideJudgement( uint32 regIndex, address target, Judgement memory judgement, bytes32 identity ) external; /// @dev Add a "sub" identity account for the caller. /// @custom:selector 98717196 /// @param sub The sub account /// @param data The associated data function addSub(address sub, Data memory data) external; /// @dev Rename a "sub" identity account of the caller. /// @custom:selector 452df561 /// @param sub The sub account /// @param data The new associated data function renameSub(address sub, Data memory data) external; /// @dev Removes a "sub" identity account of the caller. /// @custom:selector b0a323e0 /// @param sub The sub account function removeSub(address sub) external; /// @dev Removes the sender as a sub-account. /// @custom:selector d5a3c2c4 function quitSub() external; /// @dev An identity was set or reset (which will remove all judgements). /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param who Address of the target account event IdentitySet(address who); /// @dev An identity was cleared, and the given balance returned. /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param who Address of the target account event IdentityCleared(address who); /// @dev A judgement was asked from a registrar. /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param who Address of the requesting account /// @param registrarIndex The registrar's index event JudgementRequested(address who, uint32 registrarIndex); /// @dev A judgement request was retracted. /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param who Address of the target account. /// @param registrarIndex The registrar's index event JudgementUnrequested(address who, uint32 registrarIndex); /// @dev A judgement was given by a registrar. /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param target Address of the target account /// @param registrarIndex The registrar's index event JudgementGiven(address target, uint32 registrarIndex); /// @dev A sub-identity was added to an identity and the deposit paid. /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param sub Address of the sub account /// @param main Address of the main account event SubIdentityAdded(address sub, address main); /// @dev A sub-identity was removed from an identity and the deposit freed. /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param sub Address of the sub account /// @param main Address of the main account event SubIdentityRemoved(address sub, address main); /// @dev A sub-identity was cleared and the given deposit repatriated from the main identity account to the sub-identity account /// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e /// @param sub Address of the sub account event SubIdentityRevoked(address sub); } ``` The Identity Precompile contains some functions that can be called by anyone and some judgment-related functions that can only be called by a registrar. The functions that can be called by anyone are as follows: ??? function "**identity**(*address* who) - returns registration information for a given account" === "Parameters" - `who` - address of the account to query the identity information for ??? function "**superOf**(*address* who) - retrieves the super account for a sub-account. If the given account is not a sub-account, the address returned is `0x0000000000000000000000000000000000000000`" === "Parameters" - `who` - address of the account to query the super-account for ??? function "**subsOf**(*address* who) - returns the sub-accounts for a given account. If the given account doesn't have any sub-accounts, an empty array is returned (`[]`)" === "Parameters" - `who` - address of the account to query the sub-accounts for ??? function "**registrars**() - returns the list of registrars" === "Parameters" None. ??? function "**setIdentity**(*IdentityInfo memory* info) - sets the identity for the caller" === "Parameters" - `info` - IdentityInfo memory struct containing the identity information to be set ??? function "**setSubs**(*SubAccount[] memory* subs) - sets the sub-accounts for the caller" === "Parameters" - `subs` - SubAccount[] memory array containing the sub-accounts to be set ??? function "**clearIdentity**() - clears the identity for the caller" === "Parameters" None. ??? function "**requestJudgement**(*uint32* regIndex, *uint256* maxFee) - requests judgment from a given registrar along with the maximum fee the caller is willing to pay" === "Parameters" - `regIndex` - uint32 index of the registrar to request judgment from - `maxFee` - uint256 maximum fee the caller is willing to pay for the judgment ??? function "**cancelRequest**(*uint32* regIndex) - cancels the caller's request for judgment from a given registrar" === "Parameters" - `regIndex` - uint32 index of the registrar to cancel the judgment request from ??? function "**addSub**(*address* sub, *Data memory* data) - adds a sub-identity account for the caller" === "Parameters" - `sub` - address of the sub-account to add - `data` - Data memory struct containing the sub-account information ??? function "**renameSub**(*address* sub, *Data memory* data) - renames a sub-identity account for the caller" === "Parameters" - `sub` - address of the sub-account to rename - `data` - Data memory struct containing the new sub-account information ??? function "**removeSub**(*address* sub) - removes a sub identity account for the caller" === "Parameters" - `sub` - address of the sub-account to remove ??? function "**quitSub**(*address* sub) - removes the caller as a sub-identity account" === "Parameters" - `sub` - address of the sub-account to quit The judgment-related functions that must be called by a registrar and the caller must be the registrar account that corresponds to the `regIndex` are: ??? function "**setFee**(*uint32* regIndex, *uint256* fee) - sets the fee for a registrar" === "Parameters" - `regIndex` - uint32 index of the registrar setting the fee - `fee` - uint256 new fee amount to be set for the registrar ??? function "**setAccountId**(*uint32* regIndex, *address* newAccount) - sets a new account for a registrar" === "Parameters" - `regIndex` - uint32 index of the registrar being updated - `newAccount` - address of the new account to be set for the registrar ??? function "**setFields**(*uint32* regIndex, *IdentityFields memory* fields) - sets the registrar's identity" === "Parameters" - `regIndex` - uint32 index of the registrar setting their identity fields - `fields` - IdentityFields memory struct containing the identity fields to be set ??? function "**provideJudgement**(*uint32* regIndex, *address* target, *Judgement memory* judgement, *bytes32* identity) - provides judgment on an account's identity" === "Parameters" - `regIndex` - uint32 index of the registrar providing the judgment - `target` - address of the account receiving the judgment - `judgement` - Judgement memory struct containing the judgment details - `identity` - bytes32 hash of the identity information being judged ## Interact with the Solidity Interface {: #interact-with-interface } The following sections will cover how to interact with the Identity Precompile using [Ethereum libraries](/builders/ethereum/libraries/){target=\_blank}, such as [Ethers.js](/builders/ethereum/libraries/ethersjs/){target=\_blank}, [Web3.js](/builders/ethereum/libraries/web3js/){target=\_blank}, and [Web3.py](/builders/ethereum/libraries/web3py/){target=\_blank}. The examples in this guide will be on Moonbase Alpha. To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported [Endpoint Providers](/builders/get-started/endpoints/){target=\_blank}. ### Using Ethereum Libraries {: #use-ethereum-libraries } To interact with the Identity Precompile's Solidity interface with an Ethereum library, you'll need the Identity Precompile's ABI. ??? code "Identity Precompile ABI" ```js [ { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "who", "type": "address" } ], "name": "IdentityCleared", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "who", "type": "address" } ], "name": "IdentitySet", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "target", "type": "address" }, { "indexed": false, "internalType": "uint32", "name": "registrarIndex", "type": "uint32" } ], "name": "JudgementGiven", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "who", "type": "address" }, { "indexed": false, "internalType": "uint32", "name": "registrarIndex", "type": "uint32" } ], "name": "JudgementRequested", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "who", "type": "address" }, { "indexed": false, "internalType": "uint32", "name": "registrarIndex", "type": "uint32" } ], "name": "JudgementUnrequested", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "sub", "type": "address" }, { "indexed": false, "internalType": "address", "name": "main", "type": "address" } ], "name": "SubIdentityAdded", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "sub", "type": "address" }, { "indexed": false, "internalType": "address", "name": "main", "type": "address" } ], "name": "SubIdentityRemoved", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "address", "name": "sub", "type": "address" } ], "name": "SubIdentityRevoked", "type": "event" }, { "inputs": [ { "internalType": "address", "name": "sub", "type": "address" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "data", "type": "tuple" } ], "name": "addSub", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint32", "name": "regIndex", "type": "uint32" } ], "name": "cancelRequest", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "clearIdentity", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "who", "type": "address" } ], "name": "identity", "outputs": [ { "components": [ { "internalType": "bool", "name": "isValid", "type": "bool" }, { "components": [ { "internalType": "uint32", "name": "registrarIndex", "type": "uint32" }, { "components": [ { "internalType": "bool", "name": "isUnknown", "type": "bool" }, { "internalType": "bool", "name": "isFeePaid", "type": "bool" }, { "internalType": "uint256", "name": "feePaidDeposit", "type": "uint256" }, { "internalType": "bool", "name": "isReasonable", "type": "bool" }, { "internalType": "bool", "name": "isKnownGood", "type": "bool" }, { "internalType": "bool", "name": "isOutOfDate", "type": "bool" }, { "internalType": "bool", "name": "isLowQuality", "type": "bool" }, { "internalType": "bool", "name": "isErroneous", "type": "bool" } ], "internalType": "struct Identity.Judgement", "name": "judgement", "type": "tuple" } ], "internalType": "struct Identity.JudgementInfo[]", "name": "judgements", "type": "tuple[]" }, { "internalType": "uint256", "name": "deposit", "type": "uint256" }, { "components": [ { "components": [ { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "key", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "value", "type": "tuple" } ], "internalType": "struct Identity.Additional[]", "name": "additional", "type": "tuple[]" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "display", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "legal", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "web", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "riot", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "email", "type": "tuple" }, { "internalType": "bool", "name": "hasPgpFingerprint", "type": "bool" }, { "internalType": "bytes", "name": "pgpFingerprint", "type": "bytes" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "image", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "twitter", "type": "tuple" } ], "internalType": "struct Identity.IdentityInfo", "name": "info", "type": "tuple" } ], "internalType": "struct Identity.Registration", "name": "", "type": "tuple" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint32", "name": "regIndex", "type": "uint32" }, { "internalType": "address", "name": "target", "type": "address" }, { "components": [ { "internalType": "bool", "name": "isUnknown", "type": "bool" }, { "internalType": "bool", "name": "isFeePaid", "type": "bool" }, { "internalType": "uint256", "name": "feePaidDeposit", "type": "uint256" }, { "internalType": "bool", "name": "isReasonable", "type": "bool" }, { "internalType": "bool", "name": "isKnownGood", "type": "bool" }, { "internalType": "bool", "name": "isOutOfDate", "type": "bool" }, { "internalType": "bool", "name": "isLowQuality", "type": "bool" }, { "internalType": "bool", "name": "isErroneous", "type": "bool" } ], "internalType": "struct Identity.Judgement", "name": "judgement", "type": "tuple" }, { "internalType": "bytes32", "name": "identity", "type": "bytes32" } ], "name": "provideJudgement", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "quitSub", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "registrars", "outputs": [ { "components": [ { "internalType": "bool", "name": "isValid", "type": "bool" }, { "internalType": "uint32", "name": "index", "type": "uint32" }, { "internalType": "address", "name": "account", "type": "address" }, { "internalType": "uint256", "name": "fee", "type": "uint256" }, { "components": [ { "internalType": "bool", "name": "display", "type": "bool" }, { "internalType": "bool", "name": "legal", "type": "bool" }, { "internalType": "bool", "name": "web", "type": "bool" }, { "internalType": "bool", "name": "riot", "type": "bool" }, { "internalType": "bool", "name": "email", "type": "bool" }, { "internalType": "bool", "name": "pgpFingerprint", "type": "bool" }, { "internalType": "bool", "name": "image", "type": "bool" }, { "internalType": "bool", "name": "twitter", "type": "bool" } ], "internalType": "struct Identity.IdentityFields", "name": "fields", "type": "tuple" } ], "internalType": "struct Identity.Registrar[]", "name": "", "type": "tuple[]" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "sub", "type": "address" } ], "name": "removeSub", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "sub", "type": "address" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "data", "type": "tuple" } ], "name": "renameSub", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint32", "name": "regIndex", "type": "uint32" }, { "internalType": "uint256", "name": "maxFee", "type": "uint256" } ], "name": "requestJudgement", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint32", "name": "regIndex", "type": "uint32" }, { "internalType": "address", "name": "newAccount", "type": "address" } ], "name": "setAccountId", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint32", "name": "regIndex", "type": "uint32" }, { "internalType": "uint256", "name": "fee", "type": "uint256" } ], "name": "setFee", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint32", "name": "regIndex", "type": "uint32" }, { "components": [ { "internalType": "bool", "name": "display", "type": "bool" }, { "internalType": "bool", "name": "legal", "type": "bool" }, { "internalType": "bool", "name": "web", "type": "bool" }, { "internalType": "bool", "name": "riot", "type": "bool" }, { "internalType": "bool", "name": "email", "type": "bool" }, { "internalType": "bool", "name": "pgpFingerprint", "type": "bool" }, { "internalType": "bool", "name": "image", "type": "bool" }, { "internalType": "bool", "name": "twitter", "type": "bool" } ], "internalType": "struct Identity.IdentityFields", "name": "fields", "type": "tuple" } ], "name": "setFields", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "components": [ { "components": [ { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "key", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "value", "type": "tuple" } ], "internalType": "struct Identity.Additional[]", "name": "additional", "type": "tuple[]" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "display", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "legal", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "web", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "riot", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "email", "type": "tuple" }, { "internalType": "bool", "name": "hasPgpFingerprint", "type": "bool" }, { "internalType": "bytes", "name": "pgpFingerprint", "type": "bytes" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "image", "type": "tuple" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "twitter", "type": "tuple" } ], "internalType": "struct Identity.IdentityInfo", "name": "info", "type": "tuple" } ], "name": "setIdentity", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "components": [ { "internalType": "address", "name": "account", "type": "address" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "data", "type": "tuple" } ], "internalType": "struct Identity.SubAccount[]", "name": "subs", "type": "tuple[]" } ], "name": "setSubs", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "who", "type": "address" } ], "name": "subsOf", "outputs": [ { "components": [ { "internalType": "uint256", "name": "deposit", "type": "uint256" }, { "internalType": "address[]", "name": "accounts", "type": "address[]" } ], "internalType": "struct Identity.SubsOf", "name": "", "type": "tuple" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "who", "type": "address" } ], "name": "superOf", "outputs": [ { "components": [ { "internalType": "bool", "name": "isValid", "type": "bool" }, { "internalType": "address", "name": "account", "type": "address" }, { "components": [ { "internalType": "bool", "name": "hasData", "type": "bool" }, { "internalType": "bytes", "name": "value", "type": "bytes" } ], "internalType": "struct Identity.Data", "name": "data", "type": "tuple" } ], "internalType": "struct Identity.SuperOf", "name": "", "type": "tuple" } ], "stateMutability": "view", "type": "function" } ] ``` Once you have the ABI, you can interact with the precompile using the Ethereum library of your choice. Generally speaking, you'll take the following steps: 1. Create a provider 2. Create a contract instance of the Identity Precompile 3. Interact with the Identity Precompile's functions In the examples below, you'll learn how to assemble the data required to set an identity, how to set an identity, and how to retrieve the identity information once it's been set. !!! remember The following snippets are for demo purposes only. Never store your private keys in a JavaScript or Python file. === "Ethers.js" ```js import { ethers } from 'ethers'; // Import Ethers library import ABI from './identityPrecompileABI.js'; // Import Identity Precompile ABI const privateKey = 'INSERT_PRIVATE_KEY'; const identityPrecompileAddress = '0x0000000000000000000000000000000000000818'; // Create Ethers provider and signer const provider = new ethers.JsonRpcProvider( 'https://rpc.api.moonbase.moonbeam.network' ); const signer = new ethers.Wallet(privateKey, provider); // Create interface for the Identity Precompile const identityPrecompile = new ethers.Contract( identityPrecompileAddress, ABI, signer ); // Interact with the Precompile Registry const setIdentity = async () => { // Assemble identity info const identityInfo = { additional: [], display: { hasData: true, value: '0x416c696365', // Alice in hex }, legal: { hasData: false, value: '0x', }, web: { hasData: false, value: '0x', }, riot: { hasData: false, value: '0x', }, email: { hasData: false, value: '0x', }, hasPgpFingerprint: false, pgpFingerprint: '0x', image: { hasData: false, value: '0x', }, twitter: { hasData: false, value: '0x', }, }; // Set the identity const submitIdentity = await identityPrecompile.setIdentity(identityInfo); console.log(`Identity set. Transaction hash: ${submitIdentity.hash}`); // Retrieve the identity const identity = await identityPrecompile.identity(signer.address); console.log(`Identity is valid: ${identity[0]}`); console.log(`Judgements provided for this identity: ${identity[1]}`); console.log(`Deposit paid for this identity: ${identity[2]}`); console.log(`Identity information: ${identity[3]}`); console.log(`Display name: ${ethers.toUtf8String(identity[3][1][1])}`); }; setIdentity(); ``` === "Web3.js" ```js import { Web3 } from 'web3'; import ABI from './identityPrecompileABI.js'; // Import Identity Precompile ABI const from = { privateKey: 'INSERT_PRIVATE_KEY', address: 'INSERT_ADDRESS', }; const identityPrecompileAddress = '0x0000000000000000000000000000000000000818'; // Create provider const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network'); // Create interface for the Identity Precompile const identityPrecompile = new web3.eth.Contract( ABI, identityPrecompileAddress, { from: from.address } ); // Interact with the Precompile Registry const setIdentity = async () => { // Assemble identity info const identityInfo = { additional: [], display: { hasData: true, value: '0x416c696365', // Alice in hex }, legal: { hasData: false, value: '0x', }, web: { hasData: false, value: '0x', }, riot: { hasData: false, value: '0x', }, email: { hasData: false, value: '0x', }, hasPgpFingerprint: false, pgpFingerprint: '0x', image: { hasData: false, value: '0x', }, twitter: { hasData: false, value: '0x', }, }; // Set the identity const submitIdentity = await identityPrecompile.methods.setIdentity( identityInfo ); const sendTransaction = await web3.eth.accounts.signTransaction( { to: identityPrecompileAddress, data: submitIdentity.encodeABI(), gas: await submitIdentity.estimateGas(), gasPrice: await web3.eth.getGasPrice(), nonce: await web3.eth.getTransactionCount(from.address), }, from.privateKey ); // Sign and send the transaction to set the identity const createReceipt = await web3.eth.sendSignedTransaction( sendTransaction.rawTransaction ); console.log( `Identity set. Transaction hash: ${createReceipt.transactionHash}` ); // Retrieve the identity const identity = await identityPrecompile.methods.identity(address).call(); console.log(`Identity is valid: ${identity[0]}`); console.log(`Judgements provided for this identity: ${identity[1]}`); console.log(`Deposit paid for this identity: ${identity[2]}`); console.log(`Identity information: ${identity[3]}`); console.log(`Display name: ${web3.utils.hexToUtf8(identity[3][1][1])}`); }; setIdentity(); ``` === "Web3.py" ```py from web3 import Web3 # Paste or import the Identity Precompile ABI abi = "INSERT_IDENTITY_PRECOMPILE_ABI" account_from = { "private_key": "INSERT_PRIVATE_KEY", "address": "INSERT_ADDRESS", } identity_precompile_address = "0x0000000000000000000000000000000000000818" # Create provider web3 = Web3(Web3.HTTPProvider("https://rpc.api.moonbase.moonbeam.network")) # Create interface for the Precompile Registry identity_precompile = web3.eth.contract(address=identity_precompile_address, abi=abi) def set_identity(): # Assemble identity info identity_info = { "additional": [], "display": { "hasData": True, "value": "0x416c696365", # Alice in hex }, "legal": { "hasData": False, "value": "0x", }, "web": { "hasData": False, "value": "0x", }, "riot": { "hasData": False, "value": "0x", }, "email": { "hasData": False, "value": "0x", }, "hasPgpFingerprint": False, "pgpFingerprint": "0x", "image": { "hasData": False, "value": "0x", }, "twitter": { "hasData": False, "value": "0x", }, } # Set the identity submit_identity = identity_precompile.functions.setIdentity( identity_info ).build_transaction( { "from": Web3.to_checksum_address(account_from["address"]), "nonce": web3.eth.get_transaction_count( Web3.to_checksum_address(account_from["address"]) ), } ) # Sign and send the transaction to set the identity tx_create = web3.eth.account.sign_transaction( submit_identity, account_from["private_key"] ) tx_hash = web3.eth.send_raw_transaction(tx_create.rawTransaction) tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash) print(f"Identity set. Transaction hash: { tx_receipt.transactionHash.hex() }") # Retrieve the identity identity = identity_precompile.functions.identity(account_from["address"]).call() print(f"Identity is valid: { identity[0] }") print(f"Judgements provided for this identity: { identity[1] }") print(f"Deposit paid for this identity: { identity[2] }") print(f"Identity information: { identity[3] }") print(f"Display name: { web3.to_text(identity[3][1][1]) }") set_identity() ``` --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/account/proxy/ --- BEGIN CONTENT --- --- title: Interacting with the Proxy Precompile description: How to use the Moonbeam proxy Solidity precompile interface to add and remove proxy accounts from Substrate's Proxy Pallet. keywords: solidity, ethereum, proxy, moonbeam, precompiled, contracts, substrate categories: Precompiles, Ethereum Toolkit --- # Interacting with the Proxy Precompile ## Introduction {: #introduction } The Proxy Precompile on Moonbeam allows accounts to set proxy accounts that can perform specific limited actions on their behalf, such as governance, staking, or balance transfers. If a user wanted to provide a second user access to a limited number of actions on their behalf, traditionally the only method to do so would be by providing the first account's private key to the second. However, Moonbeam has included the [Substrate Proxy Pallet](/builders/substrate/interfaces/account/proxy/){target=\_blank}, which enables proxy accounts. Proxy accounts ought to be used due to the additional layer of security that they provide, where many accounts can perform actions for a main account. This is best if, for example, a user wants to keep their wallet safe in cold storage but still wants to access parts of the wallet's functionality like governance or staking. **The Proxy Precompile can only be called from an Externally Owned Account (EOA) or by the [Batch Precompile](/builders/ethereum/precompiles/ux/batch/){target=\_blank}.** To learn more about proxy accounts and how to set them up for your own purposes without use of the Proxy Precompile, view the [Setting up a Proxy Account](/tokens/manage/proxy-accounts/){target=\_blank} page. The Proxy Precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.proxy}} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.proxy}} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.proxy}} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Proxy Solidity Interface {: #the-proxy-solidity-interface } [`Proxy.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/proxy/Proxy.sol){target=\_blank} is an interface through which Solidity contracts can interact with the Proxy Pallet. You do not have to be familiar with the Substrate API since you can interact with it using the Ethereum interface you're familiar with. The interface includes the following functions: ??? function "**addProxy**(*address* delegate, *ProxyType* proxyType, *uint32* delay) - registers a proxy account for the sender after a specified number of `delay` blocks (generally zero). Will fail if a proxy for the caller already exists" === "Parameters" - `delegate` - address of the account to be registered as a proxy - `proxyType` - ProxyType enumeration specifying the type of proxy to be registered - `delay` - uint32 number of blocks before the proxy registration becomes active ??? function "**removeProxy**(*address* delegate, *ProxyType* proxyType, *uint32* delay) - removes a registered proxy for the sender" === "Parameters" - `delegate` - address of the proxy account to be removed - `proxyType` - ProxyType enumeration of the proxy type to be removed - `delay` - uint32 delay value of the proxy to be removed ??? function "**removeProxies**() - removes all of the proxy accounts delegated to the sender" === "Parameters" None. ??? function "**isProxy**(*address* real, *address* delegate, *ProxyType* proxyType, *uint32* delay) - returns a boolean, `true` if the delegate address is a proxy of type `proxyType`, for address `real`, with the specified `delay`" === "Parameters" - `real` - address of the account that might be represented by the proxy - `delegate` - address of the potential proxy account - `proxyType` - ProxyType enumeration of the proxy type to check - `delay` - uint32 delay value to check The `proxyType` parameter is defined by the following `ProxyType` enum, where the values start at `0` with the most permissive proxy type and are represented as `uint8` values: ```solidity enum ProxyType { Any, NonTransfer, Governance, Staking, CancelProxy, Balances, AuthorMapping, IdentityJudgement } ``` ## Proxy Types {: #proxy-types } There are multiple types of proxy roles that can be delegated to accounts, where are represented in `Proxy.sol` through the `ProxyType` enum. The following list includes all of the possible proxies and the type of transactions they can make on behalf of the primary account: - **Any** — the any proxy will allow the proxy account to make any type of transaction that the `Governance`, `Staking`, `Balances`, and `AuthorMapping` proxy types can perform. Note that balance transfers are only allowed to EOAs, not contracts or Precompiles - **NonTransfer** — the non-transfer proxy will allow the proxy account to make any type of transaction through the `Governance`, `Staking` and `AuthorMapping` Precompiles, where the `msg.value` is checked to be zero - **Governance** - the governance proxy will allow the proxy account to make any type of governance related transaction (includes both democracy or council pallets) - **Staking** - the staking proxy will allow the proxy account to make staking related transactions through the `Staking` Precompile, including calls to the `AuthorMapping` Precompile - **CancelProxy** - the cancel proxy will allow the proxy account to reject and remove delayed proxy announcements (of the primary account). Currently, this is not an action supported by the Proxy Precompile - **Balances** - the balances proxy will allow the proxy account to only make balance transfers to EOAs - **AuthorMapping** - this type of proxy account is used by collators to migrate services from one server to another - **IdentityJudgement** - the identity judgement proxy will allow the proxy account to judge and certify the personal information associated with accounts on Polkadot. Currently, this is not an action supported by the Proxy Precompile ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } The following section will cover how to interact with the Proxy Precompile from Remix. Please note that **the Proxy Precompile can only be called from an EOA or by the [Batch Precompile](/builders/ethereum/precompiles/ux/batch/){target=\_blank}**. ### Checking Prerequisites {: #checking-prerequisites } The below example is demonstrated on Moonbase Alpha, however, similar steps can be taken for Moonbeam and Moonriver. You should: - Have MetaMask installed and [connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - Have an account with some DEV tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} - Have a second account that you control to use as a proxy account (funding optional) ### Remix Set Up {: #remix-set-up } To get started, get a copy of [`Proxy.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/proxy/Proxy.sol){target=\_blank} and take the following steps: 1. Click on the **File explorer** tab 2. Copy and paste the file contents into a [Remix file](https://remix.ethereum.org){target=\_blank} named `Proxy.sol` ![Copying and Pasting the Proxy Interface into Remix](/images/builders/ethereum/precompiles/account/proxy/proxy-1.webp) ### Compile the Contract {: #compile-the-contract } 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile Proxy.sol** ![Compiling Proxy.sol](/images/builders/ethereum/precompiles/account/proxy/proxy-2.webp) ### Access the Contract {: #access-the-contract } 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **Proxy.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the Precompile in the **At Address** field 4. Provide the address of the Proxy Precompile for Moonbase Alpha: `{{networks.moonbase.precompiles.proxy}}` and click **At Address** 5. The Proxy Precompile will appear in the list of **Deployed Contracts** ![Provide the address](/images/builders/ethereum/precompiles/account/proxy/proxy-3.webp) ### Add a Proxy {: #add-proxy } You can add a proxy for your account via the Proxy Precompile if your account doesn't already have a proxy. In this example, you will add a [balances](#:~:text=Balances) proxy to an account by taking the following steps: 1. Expand the Proxy Precompile contract to see the available functions 2. Find the **addProxy** function and press the button to expand the section 3. Insert your second account's address as the **delegate**, `5` as **proxyType**, and `0` as **delay** 4. Press **transact** and confirm the transaction in MetaMask !!! note When constructing the transaction in Remix, the **proxyType** is represented as a `uint8`, instead of the expected enum `ProxyType`. In Solidity, enums are compiled as `uint8`, so when you pass in `5` for **proxyType**, you indicate the sixth element in the `ProxyType` enum, which is the balances proxy. ![Call the addProxy function](/images/builders/ethereum/precompiles/account/proxy/proxy-4.webp) ### Check a Proxy's Existence {: #check-proxy } You can determine whether or not an account is a proxy account for a primary account. In this example, you will insert the parameters of the [previously added proxy](#add-proxy) to determine if the proxy account was successfully added: 1. Find the **isProxy** function and press the button to expand the section 2. Insert your primary account's address as **real**, your second account's address as **delegate**, `5` as **proxyType**, and `0` as **delay** 3. Press **call** If everything went correctly, the output should be `true`. ![Call the isProxy function](/images/builders/ethereum/precompiles/account/proxy/proxy-5.webp) ### Remove a Proxy {: #remove-proxy } You can remove a proxy from your account via the Proxy Precompile. In this example, you will remove the balances proxy [previously added](#add-proxy) to your delegate account by taking the following steps: 1. Expand the Proxy Precompile contract to see the available functions 2. Find the **removeProxy** function and press the button to expand the section 3. Insert your second account's address as the **delegate**, `5` as **proxyType**, `0` and as **delay** 4. Press **transact** and confirm the transaction in MetaMask After the transaction is confirmed, if you repeat the steps to [check for a proxy's existence](#check-proxy), the result should be `false`. ![Call the removeProxy function](/images/builders/ethereum/precompiles/account/proxy/proxy-6.webp) And that's it! You've completed your introduction to the Proxy Precompile. Additional information on setting up proxies is available on the [Setting up a Proxy Account](/tokens/manage/proxy-accounts/){target=\_blank} page and the [Proxy Accounts](https://wiki.polkadot.com/learn/learn-proxies/){target=\_blank} page on Polkadot's documentation. Feel free to reach out on [Discord](https://discord.com/invite/PfpUATX){target=\_blank} if you have any questions about any aspect of the Proxy Precompile. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/features/governance/collective/ --- BEGIN CONTENT --- --- title: Collective Precompile Contract description: Learn how to use the Moonbeam Collective Precompile to perform democracy functions through any of the collectives on Moonbeam, such as the Treasury Council. keywords: solidity, ethereum, collective, proposal, council technical, committee, moonbeam, precompiled, contracts categories: Precompiles, Ethereum Toolkit --- # Interacting with the Collective Precompile ## Introduction {: #introduction } The Collective Precompile enables a user to directly interact with [Substrate's collective pallet](https://paritytech.github.io/substrate/master/pallet_collective/index.html){target=\_blank} directly from a Solidity interface. A collective is a group of members that are responsible for specific democracy-related actions such as proposing, voting on, executing, and closing motions. Each can execute different actions with different origins. Consequently, collectives can be created with very specific scopes. There are currently two collectives: the Treasury Council collective and the OpenGov Technical Committee collective. As such, there is a precompile for each collective. For more information on the OpenGov Technical Committee please refer to the [Governance on Moonbeam](/learn/features/governance/){target=\_blank} page, and for more information on the Treasury Council, please refer to the [Treasury on Moonbeam](/learn/features/treasury/){target=\_blank} page. This guide will show you how to propose, vote on, and close a proposal using the Collective Precompile. The Collective Precompiles are located at the following addresses: === "Moonbeam" | Collective | Address | |:---------------------------:|:--------------------------------------------------------------------:| | Treasury Council | {{networks.moonbeam.precompiles.collective_treasury }} | | OpenGov Technical Committee | {{networks.moonbeam.precompiles.collective_opengov_tech_committee }} | === "Moonriver" | Collective | Address | |:---------------------------:|:---------------------------------------------------------------------:| | Treasury Council | {{networks.moonriver.precompiles.collective_treasury }} | | OpenGov Technical Committee | {{networks.moonriver.precompiles.collective_opengov_tech_committee }} | === "Moonbase Alpha" | Collective | Address | |:---------------------------:|:--------------------------------------------------------------------:| | Treasury Council | {{networks.moonbase.precompiles.collective_treasury }} | | OpenGov Technical Committee | {{networks.moonbase.precompiles.collective_opengov_tech_committee }} | !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Collective Solidity Interface {: #the-call-permit-interface } [`Collective.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's five methods. The interface includes the following functions: ??? function "**execute**(*bytes memory* proposal) - executes a proposal as a single member of the collective. The sender must be a member of the collective. This will *not* revert if the Substrate proposal is dispatched but fails" === "Parameters" - `proposal` - bytes memory containing the [SCALE encoded](https://docs.polkadot.com/polkadot-protocol/parachain-basics/data-encoding/){target=\_blank} Substrate call that proposes an action ??? function "**propose**(*uint32* threshold, *bytes memory* proposal) - adds a new proposal to be voted on. The sender must be a member of the collective. If the threshold is less than two then the proposal will be dispatched and executed directly, with the proposer as dispatcher. If the threshold is met, the index of the new proposal is returned" === "Parameters" - `threshold` - uint32 amount of members required to dispatch the proposal - `proposal` - bytes memory containing the [SCALE encoded](https://docs.polkadot.com/polkadot-protocol/parachain-basics/data-encoding/){target=\_blank} Substrate call that proposes an action ??? function "**vote**(*bytes32* proposalHash, *uint32* proposalIndex, *bool* approve) - votes on a proposal. The sender must be a member of the collective" === "Parameters" - `proposalHash` - bytes32 hash of the proposal - `proposalIndex` - uint32 index of the proposal - `approve` - bool indicating the vote to approve the proposal or not ??? function "**close**(*bytes32* proposalHash, *uint32* proposalIndex, *uint64* proposalWeightBound, *uint32* lengthBound) - closes a proposal. Can be called by anyone once there are enough votes. Returns a boolean indicating whether the proposal was executed or removed" === "Parameters" - `proposalHash` - bytes32 hash of the proposal - `proposalIndex` - uint32 index of the proposal - `proposalWeightBound` - uint64 maximum amount of Substrate weight the proposal can use. If the proposal call uses more, the call will revert - `lengthBound` - uint32 value higher or equal to the length of the SCALE encoded proposal in bytes ??? function "**proposalHash**(*bytes memory* proposal) - computes the hash of a proposal" === "Parameters" - `proposal` - bytes memory containing the [SCALE encoded](https://docs.polkadot.com/polkadot-protocol/parachain-basics/data-encoding/){target=\_blank} Substrate call that proposes an action The interface includes the following events: - **Executed**(*bytes32* proposalHash) - emitted when a proposal is executed - **Proposed**(*address indexed* who, *uint32* indexed proposalIndex, *bytes32 indexed* proposalHash, *uint32* threshold) - emitted when a proposal has successfully been proposed and can be executed or voted on - **Voted**(*address indexed* who, *bytes32 indexed proposalHash, *bool* voted) - emitted when a proposal is voted on - **Closed**(*bytes32 indexed* proposalHash) - emitted when a proposal has been closed ## Interacting with the Solidity Interface {: #interacting-with-the-solidity-interface } The example in this section will show you how to submit a Treasury proposal using the Treasury Council Collective Precompile. As such, the proposal will be subject to meeting the voting requirements of the Treasury Council. The threshold for accepting a Treasury proposal is at least three-fifths of the Treasury Council. On the other hand, the threshold for rejecting a proposal is at least one-half of the Treasury Council. Please keep in mind that in order to propose and vote on the proposal, you must be a member of the Treasury Council. If you are not a member of the Treasury Council on Moonbeam, Moonriver, or Moonbase Alpha, you can test out the features of the Collective Precompile using a [Moonbeam development node](/builders/get-started/networks/moonbeam-dev/){target=\_blank}. The Moonbeam development node comes with [ten pre-funded accounts](/builders/get-started/networks/moonbeam-dev/#pre-funded-development-accounts){target=\_blank}, of which Baltathar, Charleth, and Dorothy are automatically set as members of the Treasury Council collective. You can use any of these three accounts to follow along with the rest of the guide. ### Checking Prerequisites {: #checking-prerequisites } The example in this guide will be shown on a Moonbeam development node, however, it can be adapted for any of the Moonbeam-based networks. To get started, you will need to have the following: - Have MetaMask installed and [connected to one of the Moonbeam-based networks](/tokens/connect/metamask/){target=\_blank} - Have an account with funds. If using a Moonbeam development node, the development accounts are pre-funded. For Moonbeam, Moonriver, or Moonbase Alpha, you'll need to fund your account. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} If you're using a Moonbeam development node and the development accounts, you'll also need to do the following: - Set your development node to seal blocks on a time interval such as every 6 seconds (6000 milliseconds) using the `--sealing 6000` flag - [Connect Polkadot.js Apps to your local Moonbeam development node](/builders/get-started/networks/moonbeam-dev/#connecting-polkadot-js-apps-to-a-local-moonbeam-node){target=\_blank} - Import Baltathar's, Charleth's, and/or Dorothy's accounts into [Polkadot.js Apps](/tokens/connect/polkadotjs/#creating-or-importing-an-h160-account){target=\_blank} and [MetaMask](/tokens/connect/metamask/#import-accounts){target=\_blank} ### Remix Set Up {: #remix-set-up } 1. Get a copy of [`Collective.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/collective/Collective.sol){target=\_blank} 2. Copy and paste the file contents into a [Remix file](https://remix.ethereum.org){target=\_blank} named `Collective.sol` ![Copying and Pasting the Collective Interface into Remix](/images/builders/ethereum/precompiles/features/governance/collective/collective-1.webp) ### Compile the Contract {: #compile-the-contract } 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile Collective.sol** ![Compiling Collective.sol](/images/builders/ethereum/precompiles/features/governance/collective/collective-2.webp) ### Access the Contract {: #access-the-contract } 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: You are not deploying a contract here; instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **Collective - Collective.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the **At Address** Field 4. Provide the address of the Collective Precompile,`{{networks.moonbase.precompiles.collective_treasury}}`, and click **At Address** 5. The Collective Precompile will appear in the list of **Deployed Contracts** ![Access the precompile contract](/images/builders/ethereum/precompiles/features/governance/collective/collective-3.webp) ### Create a Proposal {: #create-a-proposal } In order to submit a proposal to be voted on by the Treasury Council collective, you must first create a Treasury proposal. If a Treasury proposal that you want to vote on already exists and you have the proposal index, you can skip ahead to the next section. To submit a Treasury proposal, you can do so via the [Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/treasury){target=\_blank} Treasury page. For this example, you can create a simple proposal to send Alith 10 DEV tokens that can be used to host a community event. To get started, click on **Submit proposal**, and fill out the following information: 1. From the **submit with account** dropdown, select the account you want to submit the proposal with. The bond for the proposal will be taken from this account 2. Select the **beneficiary**, which can be **Alith** for this example 3. Enter `10` for the **value** 4. Click on **Submit proposal** and then sign and submit the proposal ![Submit a treasury proposal](/images/builders/ethereum/precompiles/features/governance/collective/collective-4.webp) You should see the proposal appear in the **proposals** section. If this is the first proposal created, it will have a proposal index of `0`, which will be needed in the next section. To view all of the proposals, you can navigate to the **Developer** tab, select **Chain State**, and take the following steps: 1. From the **selected state query** dropdown, choose **treasury** 2. Select the **proposals** extrinsic 3. Toggle the **include option** slider off 4. Click **+** to submit the query 5. The results will appear below with the proposal index and the proposal details ![View all treasury proposals](/images/builders/ethereum/precompiles/features/governance/collective/collective-5.webp) Now that you have the proposal and the proposal index, you'll be able to approve the proposal in the following section using the Collective Precompile. ### Propose the Proposal {: #propose-the-proposal } In order to propose a proposal using the Collective Precompile, so that the corresponding collective can vote on it, you will need to obtain the encoded call data of the call, to be executed by proposal. You can get the encoded call data from Polkadot.js Apps. For this example, you need to propose the **approveProposal** extrinsic of the treasury pallet. To do so, navigate to the **Developer** tab, select **Extrinsics**, and take the following steps: 1. Select an account (any account is fine because you're not submitting any transaction here) 2. Select the **treasury** pallet 3. Choose the **approveProposal** extrinsic 4. Enter the proposal index that the collective will vote on to approve 5. Copy the **encoded call data** for the proposal ![Get encoded proposal](/images/builders/ethereum/precompiles/features/governance/collective/collective-6.webp) For this example, the extrinsic encoded call data for the proposal in this example is `0x110200`. With the encoded proposal, you can head back to Remix and expand the **COLLECTIVE** precompile contract under the **Deployed Contracts** section. Make sure you're connected to your account that is a member of the Treasury Council, and take the following steps to propose the approval: 1. Expand the **propose** function 2. Enter the **threshold**. Keep in mind that for Treasury proposals to be approved, at least three-fifths of the Treasury Council is needed to vote in approval. As such, you can set the threshold to `2` for this example 3. For the **proposal** field, you can paste the encoded proposal you retrieved from Polkadot.js Apps 4. Click **transact** 5. MetaMask will pop up and you can confirm the transaction ![Propose the approval](/images/builders/ethereum/precompiles/features/governance/collective/collective-7.webp) ### Vote on a Proposal {: #vote-on-a-proposal } To vote on a proposal, you'll need to get the proposal hash by passing in the encoded proposal into the **proposalHash** function. ![Get the proposal hash](/images/builders/ethereum/precompiles/features/governance/collective/collective-8.webp) Once you have the proposal hash, make sure you're connected to your account that is a member of the Treasury Council, and take the following steps to vote on a proposal: 1. Expand the **vote** function in Remix 2. Enter the **proposalHash** 3. Enter the **proposalIndex** 4. Enter `true` for the **approve** field 5. Click **transact** 6. MetaMask will pop up and you can confirm the transaction ![Vote on the proposal](/images/builders/ethereum/precompiles/features/governance/collective/collective-9.webp) With the threshold set to `2`, you'll need to switch accounts in MetaMask to another member of the Treasury Council collective and repeat the steps above to vote and meet the threshold. Once the threshold has been met, you can then close the proposal, which will automatically execute it, and if approved, the proposal enters a queue to be placed into a spend period where the proposed amount will be distributed to the beneficiary. In this case, once the proposal is placed into a spend period, 10 DEV tokens will be distributed to Alith. ## Close a Proposal {: #close-a-proposal } If a proposal has enough votes, anyone can close a proposal. You do not need to be a member of the Treasury Council in order to close a proposal. To close a proposal, you can take the following steps: 1. Expand the **close** function 2. Enter the **proposalHash** 3. Enter the **proposalIndex** 4. Enter the **proposalWeightBound**, which for this example can be `1000000000` 5. Enter the **lengthBound**, which can be a value equal to or greater than the length of the encoded call data for the proposal. For this example the encoded call data is `0x110200`, and as such, you can set this value to `8` 6. Click on **transact** 7. MetaMask will pop up and you can confirm the transaction ![Close the proposal](/images/builders/ethereum/precompiles/features/governance/collective/collective-10.webp) You can verify the proposal has been approved using Polkadot.js Apps. From the **Developer** tab, select **Chain State**, and take the following steps: 1. Select the **treasury** pallet 2. Select the **approvals** extrinsic 3. Click **+** to submit the query 4. The proposal will appear in the list of approvals ![Review the treasury approvals](/images/builders/ethereum/precompiles/features/governance/collective/collective-11.webp) Once the proposal is in a spend period, the funds will get distributed to the beneficiary, and the original bond will be returned to the proposer. If the Treasury runs out of funds, the approved proposals will remain in storage until the following spend period when the Treasury has enough funds again. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/features/governance/conviction-voting/ --- BEGIN CONTENT --- --- title: Conviction Voting Precompile Contract description: Learn how to vote on referenda, set up voting delegations, and more, directly through a Solidity interface with the Conviction Voting Precompile on Moonbeam. categories: Precompiles, Ethereum Toolkit --- # Interacting with the Conviction Voting Precompile ## Introduction {: #introduction } As a Polkadot parachain and decentralized network, Moonbeam features native on-chain governance that enables stakeholders to participate in the direction of the network. With the introduction of OpenGov, also referred to as Governance v2, the Conviction Voting Pallet allows token holders to make, delegate, and manage Conviction-weighted votes on referenda. To learn more about Moonbeam's governance system, such as an overview of related terminology, principles, mechanics, and more, please refer to the [Governance on Moonbeam](/learn/features/governance/){target=\_blank} page. The Conviction Voting Precompile interacts directly with Substrate's Conviction Voting Pallet. This pallet is coded in Rust and is normally not accessible from the Ethereum API side of Moonbeam. However, the Conviction Voting Precompile allows you to access governance-related functions of the Substrate Conviction Voting Pallet directly from a Solidity interface. Additionally, this enables a vastly improved end-user experience. For example, token holders can vote on referenda or delegate a vote directly from MetaMask, rather than importing an account in Polkadot.js Apps and navigating a complex UI. The Conviction Voting Precompile is located at the following address: === "Moonbeam" ```text {{ networks.moonbeam.precompiles.conviction_voting }} ``` === "Moonriver" ```text {{ networks.moonriver.precompiles.conviction_voting }} ``` === "Moonbase Alpha" ```text {{ networks.moonbase.precompiles.conviction_voting }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Conviction Voting Solidity Interface {: #the-conviction-voting-solidity-interface } [`ConvictionVoting.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/conviction-voting/ConvictionVoting.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's methods. The interfaces includes a `Conviction` enum that defines the [Conviction multiplier](/learn/features/governance/#conviction-multiplier-v2){target=\_blank} types. The enum has the following variables: - **None** - 0.1x votes, unlocked - **Locked1x** - 1x votes, locked for an Enactment Period following a successful vote - **Locked2x** - 2x votes, locked for 2x Enactment Period following a successful vote - **Locked3x** - 4x votes, locked for 4x Enactments Period following a successful vote - **Locked4x** - 8x votes, locked for 8x Enactments Period following a successful vote - **Locked5x** - 16x votes, locked for 16x Enactments Period following a successful vote - **Locked6x** - 32x votes, locked for 32x Enactments Period following a successful vote The interface includes the following functions: ??? function "**votingFor**(*address* who, *uint16* trackId) - returns the votes for a given account and Track" === "Parameters" - `who` - address of the account to query the votes for - `trackId` - uint16 Track ID where the requested changes need to occur ??? function "**classLocksFor**(*address* who) - returns the class locks for a given account" === "Parameters" - `who` - address of the account to query the class locks for ??? function "**voteYes**(*uint32* pollIndex, *uint256* voteAmount, *Conviction* conviction) - votes a Conviction-weighted "Aye" on a poll (referendum)" === "Parameters" - `pollIndex` - uint32 index of the poll (referendum) - `voteAmount` - uint256 balance to be locked for the vote - `conviction` - Conviction represents a value from the aforementioned `Conviction` enum ??? function "**voteNo**(*uint32* pollIndex, *uint256* voteAmount, *Conviction* conviction) - votes a Conviction-weighted "Nay" on a poll (referendum)" === "Parameters" - `pollIndex` - uint32 index of the poll (referendum) - `voteAmount` - uint256 balance to be locked for the vote - `conviction` - Conviction represents a value from the aforementioned `Conviction` enum ??? function "**voteSplit**(*uint32* pollIndex, *uint256* aye, *uint256* nay) - votes a split vote, with a given amount locked for "Aye" and a given amount locked for "Nay", on a poll (referendum)" === "Parameters" - `pollIndex` - uint32 index of the poll (referendum) - `aye` - uint256 balance to be locked for the "Aye" vote - `nay` - uint256 balance to be locked for the "Nay" vote ??? function "**voteSplitAbstain**(*uint32* pollIndex, *uint256* aye, *uint256* nay) - votes a split abstained vote, with a given amount locked for "Aye", a given amount locked for "Nay", and a given amount locked for an abstain vote (support), on a poll (referendum)" === "Parameters" - `pollIndex` - uint32 index of the poll (referendum) - `aye` - uint256 balance to be locked for the "Aye" vote - `nay` - uint256 balance to be locked for the "Nay" vote ??? function "**removeVote**(*uint32* pollIndex) - [removes a vote](/builders/substrate/interfaces/features/governance/conviction-voting/#extrinsics){target=\_blank} in a poll (referendum)" === "Parameters" - `pollIndex` - uint32 index of the poll (referendum) ??? function "**removeVoteForTrack**(*uint32* pollIndex, *uint16* trackId) - [removes a vote](/builders/substrate/interfaces/features/governance/conviction-voting/#extrinsics){target=\_blank} from a specific track in a poll (referendum)" === "Parameters" - `pollIndex` - uint32 index of the poll (referendum) - `trackId` - uint16 Track ID where the requested changes need to occur ??? function "**removeOtherVote**(*address* target, *uint16* trackId, *uint32* pollIndex) - [removes a vote](/builders/substrate/interfaces/features/governance/conviction-voting/#extrinsics){target=\_blank} in a poll (referendum) for another voter" === "Parameters" - `target` - address that has a vote or tokens to be removed or unlocked - `trackId` - uint16 Track ID where the requested changes need to occur - `pollIndex` - uint32 index of the poll (referendum) ??? function "**delegate**(*uint16* trackId, *address* representative, *Conviction* conviction, *uint256* amount) - delegates another account as a representative to place a Conviction-weighted vote on the behalf of the sending account for a specific Track" === "Parameters" - `trackId` - uint16 Track ID where the requested changes need to occur - `representative` - address of the account to be delegated as representative - `conviction` - Conviction represents a value from the aforementioned `Conviction` enum - `amount` - uint256 balance to be delegated ??? function "**undelegate**(*uint16* trackId) - removes the caller's vote delegations for a specific Track" === "Parameters" - `trackId` - uint16 Track ID where the requested changes need to occur ??? function "**unlock**(*uint16* trackId, *address* target) - unlocks the locked tokens of a specific account for a specific Track" === "Parameters" - `trackId` - uint16 Track ID where the requested changes need to occur - `target` - address that has a vote or tokens to be removed or unlocked The interface also includes the following events: - **Voted**(*uint32 indexed* pollIndex, *address* voter, *bool* aye, *uint256* voteAmount, *uint8* conviction) - emitted when an account makes a vote - **VoteSplit**(*uint32 indexed* pollIndex, *address* voter, *uin256* aye, *uint256* nay) - emitted when an account makes a split vote - **VoteSplitAbstained**(*uint32 indexed* pollIndex, *address* voter, *uin256* aye, *uint256* nay, *uint256* nay) - emitted when an account makes a split abstained vote - **VoteRemoved**(*uint32 indexed* pollIndex, *address* voter) - emitted when an account's (`voter`) vote has been removed from an ongoing poll (referendum) - **VoteRemovedForTrack**(*uint32 indexed* pollIndex, *uint16* trackId, *address* voter) - emitted when an account's (`voter`) vote has been removed from an ongoing poll (referendum) for a specific Track - **VoteRemovedOther**(*uint32 indexed* pollIndex, *address* caller, *address* target, *uint16* trackId) - emitted when an account (`caller`) removed a vote for another account (`target`) - **Delegated**(*uint16 indexed* trackId, *address* from, *address* to, *uint256* delegatedAmount, *uint8* conviction) - emitted when an account (`from`) delegates a Conviction-weighted vote of a given amount to another account (`to`) - **Undelegated**(*uint16 indexed* trackId, *address* caller) - emitted when an account's (`caller`) delegations are removed for a specific Track - **Unlocked**(*uint16 indexed* trackId, *address* caller) - emitted when an account's (`caller`) locked tokens are unlocked for a specific Track ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } ### Checking Prerequisites {: #checking-prerequisites } The below example is demonstrated on Moonbase Alpha, however, similar steps can be taken for Moonriver. To follow the steps in this guide, you'll need to have the following: - MetaMask installed and [connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - An account with some DEV tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Remix Set Up {: #remix-set-up } 1. Click on the **File explorer** tab 2. Paste a copy of [`ConvictionVoting.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/conviction-voting/ConvictionVoting.sol){target=\_blank} into a [Remix file](https://remix.ethereum.org){target=\_blank} named `ConvictionVoting.sol` ![Copy and paste the referenda Solidity interface into Remix.](/images/builders/ethereum/precompiles/features/governance/conviction-voting/conviction-voting-1.webp) ### Compile the Contract {: #compile-the-contract } 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile ConvictionVoting.sol** ![Compile the ConvictionVoting.sol interface using Remix.](/images/builders/ethereum/precompiles/features/governance/conviction-voting/conviction-voting-2.webp) ### Access the Contract {: #access-the-contract } 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **ConvictionVoting.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the **At Address** field 4. Provide the address of the Conviction Voting Precompile for Moonbase Alpha: `{{ networks.moonbase.precompiles.conviction_voting }}` and click **At Address** 5. The Conviction Voting Precompile will appear in the list of **Deployed Contracts** ![Access the ConvictionVoting.sol interface by provide the precompile's address.](/images/builders/ethereum/precompiles/features/governance/conviction-voting/conviction-voting-3.webp) ### Vote on a Referendum {: #vote-on-a-referendum } You can lock tokens and vote on a referendum at anytime during the Lead-in Period or the Decide Period. In order for a referendum to pass, it needs to garner minimum Approval and Support, which vary by track. For more information on each of the relative periods and the Approval and Support requirements by Track, please refer to the [OpenGov section of the governance overview page](/learn/features/governance/#opengov){target=\_blank}. First, you'll need to get the index of the referendum you wish to vote on. To get the index of a referendum, head to [Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network%2Fpublic-ws#/referenda){target=\_blank} and take the following steps: 1. From the **Governance** tab dropdown, select **Referenda** 2. Look for the referenda you want to vote on. You can view more details about a specific referendum by clicking on the triangle icon. If there is no triangle icon, this means that only a proposal hash, and no preimage has been submitted for the proposal 3. Take note of the referendum index ![View the list of referenda on Polkadot.js Apps.](/images/builders/ethereum/precompiles/features/governance/conviction-voting/conviction-voting-4.webp) Now, you're ready to return to Remix to vote on the referendum via the Conviction Voting Precompile. There are two methods you can use to vote on a referendum: `voteYes` or `voteNo`. As you probably have already figured out, if you're in agreement with the proposal, you'll use `voteYes` and if in disagreement, you'll use `voteNo`. You'll specify the amount of tokens you want to lock with your vote and the Conviction, using index of the Conviction you want to vote with in the [aforementioned `Conviction` enum](#the-conviction-voting-solidity-interface). For example, if you wanted to lock your tokens for the duration of two Enactment Periods following a successful vote, you would enter `2` for the `Locked2x` Conviction. For more information on Convictions, you can check out the [Conviction Multiplier section of the Governance v2 documentation](/learn/features/governance/#conviction-multiplier-v2){target=\_blank}. To submit your vote, you can take the following steps: 1. Expand the Conviction Voting Precompile contract to see the available functions if it is not already open 2. Find the **voteYes** or **voteNo** function, however you want to vote, and press the button to expand the section 3. Enter the index of the referendum to vote on 4. Enter the number of tokens to lock in Wei. Avoid entering your full balance here because you need to pay for transaction fees 5. Enter the Conviction you want to vote with 6. Press **transact** and confirm the transaction in MetaMask ![Vote on the proposal using the voteYes function of the Conviction Voting Precompile.](/images/builders/ethereum/precompiles/features/governance/conviction-voting/conviction-voting-5.webp) Once the referendum has closed, you can use the Conviction Voting precompile to remove your vote and unlock your tokens. ### Delegate a Vote {: #delegate-a-vote } In addition to voting on a referendum yourself, you can delegate a Conviction-weighted vote to someone who is more knowledgeable on a particular topic to vote on your behalf, a process known as Vote Delegation. You can even delegate a different account for each of the Tracks. To get started, you can take the following steps: 1. Find the **delegate** function and press the button to expand the section 2. Enter the Track ID of the Track that you want the delegation to be used on. Track IDs can be found in the [Referenda page of Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network%2Fpublic-ws#/referenda){target=\_blank} 3. Enter the delegate account that will have the power to vote on your behalf 4. Enter the number of tokens they can vote with in Wei. Avoid entering your full balance here because you need to pay for transaction fees 5. Enter the Conviction they can vote with 6. Press **transact** and confirm the transaction in MetaMask ![Delegate a vote using the delegate function of the Conviction Voting Precompile.](/images/builders/ethereum/precompiles/features/governance/conviction-voting/conviction-voting-6.webp) Now the delegate account can vote on your behalf! If you no longer want a delegate vote to exist, you can remove it using the `undelegate` function of the Conviction Voting Precompile. And that's it! You've completed your introduction to the Conviction Voting Precompile. There are a few more functions that are documented in [`ConvictionVoting.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/conviction-voting/ConvictionVoting.sol){target=\_blank} — feel free to reach out on [Discord](https://discord.com/invite/PfpUATX){target=\_blank} if you have any questions about those functions or any other aspect of the Conviction Voting Precompile. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/features/governance/preimage/ --- BEGIN CONTENT --- --- title: Preimage Precompile Contract description: Learn how to take the first necessary step to submit a proposal on-chain by submitting a preimage that contains the action to be carried out in the proposal. categories: Precompiles, Ethereum Toolkit --- # Interacting with the Preimage Precompile ## Introduction {: #introduction } As a Polkadot parachain and decentralized network, Moonbeam features native on-chain governance that enables stakeholders to participate in the direction of the network. With the introduction of OpenGov, also referred to as Governance v2, the Preimage Pallet allows token holders to take the first step towards creating a proposal by submitting the preimage, which is the action to be carried out in the proposal, on-chain. The hash of the preimage is required to submit the proposal. To learn more about Moonbeam's governance system, such as an overview of related terminology, the roadmap of a proposal, and more, please refer to the [Governance on Moonbeam](/learn/features/governance/){target=\_blank} page. The Preimage Precompile interacts directly with Substrate's Preimage Pallet. This pallet is coded in Rust and is normally not accessible from the Ethereum side of Moonbeam. However, the Preimage Precompile allows you to access functions needed to create and manage preimages, all of which are part of the Substrate Preimage Pallet, directly from a Solidity interface. The Preimage Precompile is located at the following address: === "Moonbeam" ```text {{ networks.moonbeam.precompiles.preimage }} ``` === "Moonriver" ```text {{ networks.moonriver.precompiles.preimage }} ``` === "Moonbase Alpha" ```text {{ networks.moonbase.precompiles.preimage }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Preimage Solidity Interface {: #the-preimage-solidity-interface } [`Preimage.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/preimage/Preimage.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's two methods: ??? function "**notePreimage**(*bytes memory* encodedPropsal) - registers a preimage on-chain for an upcoming proposal. This doesn't require the proposal to be in the dispatch queue but does require a deposit which is returned once enacted. Uses the [`notePreimage`](/builders/substrate/interfaces/features/governance/preimage/#:~:text=notePreimage(encodedProposal)){target=\_blank} method of the preimage pallet" === "Parameters" - `encodedProposal` - bytes memory containing the encoded proposal to be registered. Returns the preimage hash ??? function "**unnotePreimage**(*bytes32* hash) - clears an unrequested preimage from storage. Uses the [`unnotePreimage`](/builders/substrate/interfaces/features/governance/preimage/#:~:text=unnotePreimage(hash)){target=\_blank} method of the preimage pallet" === "Parameters" - `hash` - bytes32 hash of the preimage to be removed from storage The interface also includes the following events: - **PreimageNoted**(*bytes32* hash) - emitted when a preimage was registered on-chain - **PreimageUnnoted**(*bytes32* hash) - emitted when a preimage was un-registered on-chain ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } ### Checking Prerequisites {: #checking-prerequisites } The below example is demonstrated on Moonbase Alpha, however, similar steps can be taken for Moonriver. To follow the steps in this guide, you'll need to have the following: - MetaMask installed and [connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - An account with some DEV tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Remix Set Up {: #remix-set-up } 1. Click on the **File explorer** tab 2. Paste a copy of [`Preimage.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/preimage/Preimage.sol){target=\_blank} into a [Remix file](https://remix.ethereum.org){target=\_blank} named `Preimage.sol` ![Copy and paste the referenda Solidity interface into Remix.](/images/builders/ethereum/precompiles/features/governance/preimage/preimage-1.webp) ### Compile the Contract {: #compile-the-contract } 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile Preimage.sol** ![Compile the Preimage.sol interface using Remix.](/images/builders/ethereum/precompiles/features/governance/preimage/preimage-2.webp) ### Access the Contract {: #access-the-contract } 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **Preimage.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the **At Address** field 4. Provide the address of the Preimage Precompile for Moonbase Alpha: `{{ networks.moonbase.precompiles.preimage }}` and click **At Address** 5. The Preimage Precompile will appear in the list of **Deployed Contracts** ![Access the Preimage.sol interface by providing the precompile's address.](/images/builders/ethereum/precompiles/features/governance/preimage/preimage-3.webp) ### Submit a Preimage of a Proposal {: #submit-a-preimage } In order to submit a proposal, you'll first need to submit a preimage of that proposal, which essentially defines the proposed action on-chain. You can submit the preimage using the `notePreimage` function of the Preimage Precompile. The `notePreimage` function accepts the encoded proposal, so the first step you'll need to take is to get the encoded proposal, which can easily be done using Polkadot.js Apps. In this section, you'll get the preimage hash and the encoded proposal data for a proposal. To get the preimage hash, you'll first need to navigate to the **Preimage** page of [Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network%2Fpublic-ws#){target=\_blank}: 1. Navigate to the [**Governance** tab](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network%2Fpublic-ws#/democracy){target=\_blank} 2. Select **Preimages** from the dropdown 3. From the **Preimages** page, click on **+ Add preimage** ![Add a new preimage](/images/builders/ethereum/precompiles/features/governance/preimage/preimage-4.webp) Then take the following steps: 1. Select an account (any account is fine because you're not submitting any transaction here) 2. Choose the pallet you want to interact with and the dispatchable function (or action) to propose. The action you choose will determine the fields that need to fill in the following steps. In this example, it is the **system** pallet and the **remark** function 3. Enter the text of the remark, ensuring it is unique. Duplicate proposals such as "Hello World!" will not be accepted 4. Click the **Submit preimage** button but don't sign or confirm the transaction on the next page ![Get the proposal hash](/images/builders/ethereum/precompiles/features/governance/preimage/preimage-5.webp) On the next screen, take the following steps: 1. Press the triangle icon to reveal the encoded proposal in bytes 2. Copy the **bytes** (encoded proposal) — you'll need them when calling the `notePreimage` function ![Get the encoded proposal](/images/builders/ethereum/precompiles/features/governance/preimage/preimage-6.webp) !!! note You should NOT sign and submit the transaction here. You will submit this information via the `notePreimage` function in the next step. Now you can take the **bytes** of the encoded proposal that you got from [Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network%2Fpublic-ws#/democracy){target=\_blank} and submit it via the `notePreimage` function of the Preimage Precompile. To submit the preimage via the `notePreimage` function, take the following steps: 1. Expand the Preimage Precompile contract to see the available functions 2. Find the **notePreimage** function and press the button to expand the section 3. Provide the **bytes** of the encoded proposal that you noted in the prior section. Note, the encoded proposal is not the same as the preimage hash. Ensure you are entering the correct value into this field 4. Press **transact** and confirm the transaction in MetaMask ![Submit the preimage using the notePreimage function of the Preimage Precompile.](/images/builders/ethereum/precompiles/features/governance/preimage/preimage-7.webp) Now that you've submitted the preimage for your proposal your proposal can be submitted! Head over to the [Referenda Precompile documentation](/builders/ethereum/precompiles/features/governance/referenda/){target=\_blank} to learn how to submit your proposal. If you wish to remove a preimage, you can follow the same steps noted above except use the `unnotePreimage` function and pass in the preimage hash instead of the encoded proposal. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/features/governance/referenda/ --- BEGIN CONTENT --- --- title: Referenda Precompile Contract description: Learn how to view and submit proposals on-chain to be put forth for referenda, directly through a Solidity interface with the Referenda Precompile on Moonbeam. categories: Precompiles, Ethereum Toolkit --- # Interacting with the Referenda Precompile ## Introduction {: #introduction } As a Polkadot parachain and decentralized network, Moonbeam features native on-chain governance that enables stakeholders to participate in the direction of the network. With the introduction of OpenGov, also referred to as Governance v2, the Referenda Pallet allows token holders to get information on existing referenda, submit a proposal to be put forth for referenda, and manage actions related to the Decision Deposit, which is required for a referenda to be decided on. To learn more about Moonbeam's governance system, such as an overview of related terminology, principles, mechanics, and more, please refer to the [Governance on Moonbeam](/learn/features/governance/){target=\_blank} page. The Referenda Precompile interacts directly with Substrate's [Referenda Pallet](/builders/substrate/interfaces/features/governance/referenda/){target=\_blank}. This pallet is coded in Rust and is normally not accessible from the Ethereum side of Moonbeam. However, the Referenda Precompile allows you to access functions needed to view referenda, submit referenda, and manage the required Decision Deposit, all of which are part of the Substrate Referenda Pallet, directly from a Solidity interface. The Referenda Precompile is located at the following address: === "Moonbeam" ```text {{ networks.moonbeam.precompiles.referenda }} ``` === "Moonriver" ```text {{ networks.moonriver.precompiles.referenda }} ``` === "Moonbase Alpha" ```text {{ networks.moonbase.precompiles.referenda }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Referenda Solidity Interface {: #the-referenda-solidity-interface } [`Referenda.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/referenda/Referenda.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's methods. The methods are as follows: ??? function "**referendumCount**() - a read-only function that returns the total referendum count" === "Parameters" None. === "Returns" - `uint256` total count of referenda ??? function "**submissionDeposit**() - a read-only function that returns the Submission Deposit required for each referendum" === "Parameters" None. === "Returns" - `uint256` amount of the required Submission Deposit ??? function "**decidingCount**(*uint16* trackId) - a read-only function that returns the total count of deciding referenda for a given Track" === "Parameters" - `trackId` - uint16 Track ID to query the deciding count for === "Returns" - `uint256` count of deciding referenda for the specified Track ??? function "**trackIds**() - a read-only function that returns a list of the Track IDs for all Tracks (and Origins)" === "Parameters" None. === "Returns" - `uint16[]` array of Track IDs ??? function "**trackInfo**(*uint16* trackId) - a read-only function that returns the following governance parameters configured for a given Track ID" === "Parameters" - `trackId` - uint16 Track ID to query the parameters for === "Returns" - `string` name - the name of the Track - `uint256` maxDeciding - the maximum number of referenda that can be decided on at once - `uint256` decisionDeposit - the amount of the Decision Deposit - `uint256` preparePeriod - the duration of the Prepare Period - `uint256` decisionPeriod - the duration of the Decide Period - `uint256` confirmPeriod - the duration of the Confirm Period - `uint256` minEnactmentPeriod - the minimum amount of time the Enactment Period must be - `bytes` minApproval - the minimum "Aye" votes as a percentage of overall Conviction-weighted votes needed for an approval - `bytes` minSupport - minimum number of "Aye" votes, not taking into consideration Conviction-weighted votes, needed as a percent of the total supply needed for an approval ??? function "**referendumStatus**(*uint32* referendumIndex) - a read-only function that returns the status for a given referendum" === "Parameters" - `referendumIndex` - uint32 index of the referendum to query the status for === "Returns" ReferendumStatus enum: ```solidity enum ReferendumStatus { Ongoing, Approved, Rejected, Cancelled, TimedOut, Killed } ``` ??? function "**ongoingReferendumInfo**(*uint32* referendumIndex) - a read-only function that returns information pertaining to an ongoing referendum" === "Parameters" - `referendumIndex` - uint32 index of the ongoing referendum to query === "Returns" - `uint16` trackId - the Track of this referendum - `bytes` origin - the Origin for this referendum - `bytes` proposal - the hash of the proposal up for referendum - `bool` enactmentType - `true` if the proposal is scheduled to be dispatched *at* enactment time and `false` if *after* enactment time - `uint256` enactmentTime - the time the proposal should be scheduled for enactment - `uint256` submissionTime - the time of submission - `address` submissionDepositor - the address of the depositor for the Submission Deposit - `uint256` submissionDeposit - the amount of the Submission Deposit - `address` decisionDepositor - the address of the depositor for the Decision Deposit - `uint256` decisionDeposit - the amount of the Decision Deposit - `uint256` decidingSince - when this referendum entered the Decide Period - `uint256` decidingConfirmingEnd - when this referendum is scheduled to leave the Confirm Period - `uint256` ayes - the number of "Aye" votes, expressed in terms of post-conviction lock-vote - `uint32` support - percent of "Aye" votes, expressed pre-conviction, over total votes in the class - `uint32` approval - percent of "Aye" votes over "Aye" and "Nay" votes - `bool` inQueue - `true` if this referendum has been placed in the queue for being decided - `uint256` alarmTime - the next scheduled wake-up - `bytes` taskAddress - scheduler task address if scheduled ??? function "**closedReferendumInfo**(*uint32* referendumIndex) - a read-only function that returns information pertaining to a closed referendum" === "Parameters" - `referendumIndex` - uint32 index of the closed referendum to query === "Returns" - `uint256` end - when the referendum ended - `address` submissionDepositor - the address of the depositor for the Submission Deposit - `uint256` submissionDeposit - the amount of the Submission Deposit - `address` decisionDepositor - the address of the depositor for the Decision Deposit - `uint256` decisionDeposit - the amount of the Decision Deposit ??? function "**killedReferendumBlock**(*uint32* referendumIndex) - a read-only function that returns the block a given referendum was killed" === "Parameters" - `referendumIndex` - uint32 index of the killed referendum to query === "Returns" - `uint256` block number at which the referendum was killed ??? function "**submitAt**(*uint16* trackId, *bytes32* proposalHash, *uint32* proposalLen, *uint32* block) - submits a referendum given a Track ID corresponding to the origin from which the proposal is to be dispatched. Returns the referendum index of the submitted referendum" === "Parameters" - `trackId` - uint16 Track ID corresponding to the origin from which the proposal is to be dispatched - `proposalHash` - bytes32 preimage hash of the proposed runtime call - `proposalLen` - uint32 length of the proposal - `block` - uint32 block number *at* which this will be executed === "Returns" - `uint32` index of the submitted referendum ??? function "**submitAfter**(*uint16* trackId, *bytes32* proposalHash, *uint32* proposalLen, *uint32* block) - submits a referendum given a Track ID corresponding to the origin from which the proposal is to be dispatched. Returns the referendum index of the submitted referendum" === "Parameters" - `trackId` - uint16 Track ID corresponding to the origin from which the proposal is to be dispatched - `proposalHash` - bytes32 preimage hash of the proposed runtime call - `proposalLen` - uint32 length of the proposal - `block` - uint32 block number *after* which this will be executed === "Returns" - `uint32` index of the submitted referendum ??? function "**placeDecisionDeposit**(*uint32* index) - posts the Decision Deposit for a referendum given the index of the going referendum" === "Parameters" - `index` - uint32 index of the ongoing referendum to place the Decision Deposit for === "Returns" None. ??? function "**refundDecisionDeposit**(*uint32* index) - refunds the Decision Deposit for a closed referendum back to the depositor" === "Parameters" - `index` - uint32 index of the closed referendum in which the Decision Deposit is still locked === "Returns" None. ??? function "**refundSubmissionDeposit**(*uint32* index) - refunds the Submission Deposit for a closed referendum back to the depositor" === "Parameters" - `index` - uint32 index of the closed referendum to refund the Submission Deposit for === "Returns" None. The interface also includes the following events: - **SubmittedAt**(*uint16 indexed* trackId, *uint32* referendumIndex, *bytes32* hash) - emitted when a referenda has been submitted *at* a given block - **SubmittedAfter**(*uint16 indexed* trackId, *uint32* referendumIndex, *bytes32* hash) - emitted when a referenda has been submitted *after* a given block - **DecisionDepositPlaced**(*uint32* index, *address* caller, *uint256* depositedAmount) - emitted when a Decision Deposit for a referendum has been placed - **DecisionDepositRefunded**(*uint32* index, *address* caller, *uint256* refundedAmount) - emitted when a Decision Deposit for a closed referendum has been refunded - **SubmissionDepositRefunded**(*uint32* index, *address* caller, *uint256* refundedAmount) - emitted when a Submission Deposit for a valid referendum has been refunded ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } ### Checking Prerequisites {: #checking-prerequisites } The below example is demonstrated on Moonbase Alpha, however, similar steps can be taken for Moonriver. To follow the steps in this guide, you'll need to have the following: - MetaMask installed and [connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - An account with some DEV tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Remix Set Up {: #remix-set-up } 1. Click on the **File explorer** tab 2. Paste a copy of [`Referenda.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/referenda/Referenda.sol){target=\_blank} into a [Remix file](https://remix.ethereum.org){target=\_blank} named `Referenda.sol` ![Copy and paste the Referenda Solidity interface into Remix.](/images/builders/ethereum/precompiles/features/governance/referenda/referenda-1.webp) ### Compile the Contract {: #compile-the-contract } 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile Referenda.sol** ![Compile the Referenda.sol interface using Remix.](/images/builders/ethereum/precompiles/features/governance/referenda/referenda-2.webp) ### Access the Contract {: #access-the-contract } 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **Referenda.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the **At Address** field 4. Provide the address of the Referenda Precompile for Moonbase Alpha: `{{ networks.moonbase.precompiles.referenda }}` and click **At Address** 5. The Referenda Precompile will appear in the list of **Deployed Contracts** ![Access the Referenda.sol interface by provide the precompile's address.](/images/builders/ethereum/precompiles/features/governance/referenda/referenda-3.webp) ### Submit a Proposal {: #submit-a-proposal } In order to submit a proposal, you should have already submitted the preimage hash for the proposal. If you have not done so, please follow the steps outlined in the [Preimage Precompile](/builders/ethereum/precompiles/features/governance/preimage/){target=\_blank} documentation. There are two methods that can be used to submit a proposal: `submitAt` and `submitAfter`. The `submitAt` function submits a proposal to be executed *at* a given block and the `submitAfter` function submits a proposal to be executed *after* a specific block. For this example, `submitAt` will be used, but the same steps can be applied if you want to use `submitAfter` instead. To submit the proposal, you'll need to determine which Track your proposal belongs to and the Track ID of that Track. For help with these requirements, you can refer to the [OpenGov section of the governance overview page](/learn/features/governance/#opengov){target=\_blank}. You'll also need to make sure you have the preimage hash and the length of the preimage handy, both of which you should have received from following the steps in the [Preimage Precompile](/builders/ethereum/precompiles/features/governance/preimage/){target=\_blank} documentation. If you're unsure, you can find your preimage from the [Preimage page of Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network#/preimages){target=\_blank} and copy the preimage hash. To get the length of the preimage, you can then query the `preimage` pallet using the `preimageFor` method from the [Polkadot.js Apps Chain State page](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network#/chainstate){target=\_blank}. Once you have the Track ID, preimage hash, and preimage length, you can go ahead and submit the proposal using the Referenda Precompile. From Remix, you can take the following steps: 1. Expand the Referenda Precompile contract to see the available functions 2. Find the **submitAt** function and press the button to expand the section 3. Enter the track ID that your proposal will be processed through 4. Enter the preimage hash. You should have received this from following the steps in the [Preimage Precompile](/builders/ethereum/precompiles/features/governance/preimage/){target=\_blank} documentation 5. Enter the length of the preimage 6. Enter the block you want the proposal to be executed at 7. Press **transact** and confirm the transaction in MetaMask ![Submit the proposal using the submitAt function of the Referenda Precompile.](/images/builders/ethereum/precompiles/features/governance/referenda/referenda-4.webp) After your transaction has been confirmed you'll be able to see the proposal listed on the **Referenda** page of [Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network%2Fpublic-ws#/referenda){target=\_blank}. You can also check out your proposal on [Polkassembly](https://moonbase.polkassembly.io/opengov){target=\_blank}, which sorts proposals by the Track they belong to. ### Submit Decision Deposit {: #submit-decision-deposit } Now that you've submitted your proposal, the next step is to submit the Decision Deposit. The Decision Deposit is the minimum deposit amount required for a referendum to progress to the decision phase at the end of the Lead-in Period. For more information on the Decision Deposit, please refer to the [OpenGov section of the governance overview page](/learn/features/governance/#opengov){target=\_blank}. You can submit the Decision Deposit using the `placeDecisionDeposit` function of the Referenda Precompile. You'll just need to have the index of the referendum and enough funds to do so. The Decision Deposit varies by Track, to find the minimum amount required you can take a look at the [General Parameters by Track table on the governance overview page](/learn/features/governance/#general-parameters-by-track){target=\_blank}. To submit the deposit, you can take the following steps: 1. Find the **placeDecisionDeposit** function and press the button to expand the section 2. Enter the index of the referendum 3. Press **transact** and confirm the transaction in MetaMask ![Place the Decision Deposit for a Referenda using the placeDecisionDeposit function of the Referenda Precompile.](/images/builders/ethereum/precompiles/features/governance/referenda/referenda-5.webp) Now that the Decision Deposit has been placed, the referendum is one step closer to moving to the Decide Period. There will also need to be enough Capacity in the designated Track and the duration of the Prepare Period must pass for it to move to the Decide Period. To vote on referenda, you can follow the steps outlined in the [Conviction Voting Precompile](/builders/ethereum/precompiles/features/governance/conviction-voting/){target=\_blank} documentation. ### Refund Decision Deposit {: #refund-decision-deposit } Once a referendum has either been approved or rejected, the Decision Deposit can be refunded. This holds true as long as the referendum wasn't cancelled due to the proposal being malicious. If the proposal is deemed malicious and killed via the Root Track or the Emergency Killer Track, the Decision Deposit will be slashed. To refund the Decision Deposit, you can use the `refundDecisionDeposit` function of the Referenda Precompile. To do so, you can take the following steps: 1. Find the **refundDecisionDeposit** function and press the button to expand the section 2. Enter the index of the referendum 3. Press **transact** and confirm the transaction in MetaMask ![Refund the Decision Deposit for a Referenda using the refundDecisionDeposit function of the Referenda Precompile.](/images/builders/ethereum/precompiles/features/governance/referenda/referenda-6.webp) And that's it! You've completed your introduction to the Referenda Precompile. There are a few more functions that are documented in [`Referenda.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/referenda/Referenda.sol){target=\_blank} — feel free to reach out on [Discord](https://discord.com/invite/PfpUATX){target=\_blank} if you have any questions about those functions or any other aspect of the Referenda Precompile. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/features/randomness/ --- BEGIN CONTENT --- --- title: Randomness Precompile description: Learn about the sources of VRF randomness on Moonbeam and how to use the randomness precompile and consumer interface to generate on-chain randomness. keywords: solidity, ethereum, randomness, VRF, moonbeam, precompiled, contracts categories: Precompiles, Ethereum Toolkit --- # Interacting with the Randomness Precompile ## Introduction {: #introduction } Moonbeam utilizes verifiable random functions (VRF) to generate randomness that can be verified on-chain. A VRF is a cryptographic function that takes some input and produces random values, along with a proof of authenticity that these random values were generated by the submitter. The proof can be verified by anyone to ensure the random values generated were calculated correctly. There are two available sources of randomness that provide random inputs based on block producers' VRF keys and past randomness results: [local VRF](/learn/features/randomness/#local-vrf) and [BABE epoch randomness](/learn/features/randomness/#babe-epoch-randomness). Local VRF is determined directly within Moonbeam using the collator of the block's VRF key and the last block's VRF output. On the other hand, [BABE](https://docs.polkadot.com/polkadot-protocol/architecture/polkadot-chain/pos-consensus/#block-production-babe){target=\_blank} epoch randomness is based on all the VRF produced by the relay chain validators during a complete [epoch](https://wiki.polkadot.com/general/glossary/#epoch){target=\_blank}. For more information on the two sources of randomness, how the request and fulfillment process works, and security considerations, please refer to the [Randomness on Moonbeam](/learn/features/randomness/){target=\_blank} page. Moonbeam provides a randomness precompile, which is a Solidity interface that enables smart contract developers to generate randomness via local VRF or BABE epoch randomness using the Ethereum API. Moonbeam also provides a randomness consumer Solidity contract that your contract must inherit from in order to consume fulfilled randomness requests. This guide will show you how to use the randomness precompile and randomness consumer contract to create a lottery where the winners will randomly be selected. You'll also learn how to interact with the randomness precompile directly to perform actions such as purging an expired randomness request. The randomness precompile is located at the following address: === "Moonbeam" ```text {{ networks.moonbeam.precompiles.randomness }} ``` === "Moonriver" ```text {{ networks.moonriver.precompiles.randomness }} ``` === "Moonbase Alpha" ```text {{ networks.moonbase.precompiles.randomness }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Randomness Solidity Interface {: #the-randomness-interface } [Randomness.sol](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/randomness/Randomness.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's methods. ??? code "Randomness.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The Randomness contract's address. address constant RANDOMNESS_ADDRESS = 0x0000000000000000000000000000000000000809; /// @dev The Randomness contract's instance. Randomness constant RANDOMNESS_CONTRACT = Randomness(RANDOMNESS_ADDRESS); /// @dev Maximum number of random words being requested uint32 constant MAX_RANDOM_WORDS = 100; /// @dev Minimum number of blocks before a request can be fulfilled for Local VRF Request uint32 constant MIN_VRF_BLOCKS_DELAY = 2; /// @dev Maximum number of blocks before a request can be fulfilled for Local VRF Request uint32 constant MAX_VRF_BLOCKS_DELAY = 2000; /// @dev The deposit amount needed to request random words. There is 1 deposit per request uint256 constant REQUEST_DEPOSIT_AMOUNT = 1000000000000000000; /// @author The Moonbeam Team /// @title Pallet Randomness Interface /// @dev The interface through which solidity contracts will interact with Randomness /// @custom:address 0x0000000000000000000000000000000000000809 interface Randomness { /// @notice Event emitted when the request has been successfully executed event FulFillmentSucceeded(); /// @notice Event emitted when the request has failed to execute fulfillment event FulFillmentFailed(); /// @notice The status of the request /// @param DoesNotExist The request doesn't exist /// @param Pending The request cannot be fulfilled yet /// @param Ready The request is ready to be fulfilled /// @param Expired The request has expired enum RequestStatus { DoesNotExist, Pending, Ready, Expired } /// @notice The type of randomness source /// @param LocalVRF Randomness VRF using the parachain material as seed /// @param RelayBabeEpoch Randomness VRF using relay material from previous epoch enum RandomnessSource { LocalVRF, RelayBabeEpoch } /// @notice The request details /// @param id The id of the request (is always < 2**64) /// @param refundAddress The address receiving the left-over fees after the fulfillment /// @param contractAddress The address of the contract being called back during fulfillment /// @param fee The amount to set aside to pay for the fulfillment /// @param gasLimit The gas limit to use for the fulfillment /// @param salt A string being mixed with the randomness seed to obtain different random words. This should be as unique as possible; using the same salt will lead to same randomness result. /// @param numWords The number of random words requested (from 1 to MAX_RANDOM_WORDS) /// @param randomnessSource The type of randomness source used to generate the random words /// @param fulfillmentBlock The parachain block number at which the request can be fulfilled (for LocalVRF only) /// @param fulfillmentEpochIndex The relay epoch index at which the request can be fulfilled (for RelayBabeEpoch) /// @param expirationBlock The parachain block number at which the request expires (for LocalVRF only) /// @param expirationEpochIndex The relay epoch index at which the request expires (for RelayBabeEpoch) /// @param status The current status of the request struct Request { uint256 id; address refundAddress; address contractAddress; uint256 fee; uint256 gasLimit; bytes32 salt; uint32 numWords; RandomnessSource randomnessSource; uint32 fulfillmentBlock; uint64 fulfillmentEpochIndex; uint32 expirationBlock; uint64 expirationEpochIndex; RequestStatus status; } /// Return the current relay epoch index /// @dev An epoch represents real time and not a block number /// @dev Currently, time between epoch changes cannot be longer than: /// @dev - Kusama/Westend/Rococo: 600 relay blocks (1 hour) /// @dev - Polkadot: 2400 relay blocks (4 hours) /// @custom:selector 81797566 function relayEpochIndex() external view returns (uint64); /// Return the deposit required to perform a request /// @dev Each request will need a deposit. /// @custom:selector fb7cfdd7 function requiredDeposit() external view returns (uint256); /// @notice Returns the request status /// @param requestId The id of the request to check (must be < 2**64) /// @return status Status of the request /// @custom:selector d8a4676f function getRequestStatus(uint256 requestId) external view returns (RequestStatus status); /// @notice Returns the request or revert /// @param requestId The id of the request to check (must be < 2**64) /// @return request The request /// @custom:selector c58343ef function getRequest(uint256 requestId) external view returns (Request memory request); /// @notice Request random words generated from the parachain VRF /// @dev This is using pseudo-random VRF executed by the collator at the fulfillment /// @dev Warning: /// @dev The collator in charge of producing the block at fulfillment can decide to skip /// @dev producing the block in order to have a different random word generated by the next /// @dev collator, at the cost of a block reward. It is therefore economically viable to use /// @dev this randomness source only if the financial reward at stake is lower than the block /// @dev reward. /// @dev In order to reduce the risk of a collator being able to predict the random words /// @dev when the request is performed, it is possible to increase the delay to multiple blocks /// @dev The higher the delay is, the less likely the collator will be able to know which /// @dev collator will be in charge of fulfilling the request. /// @dev Fulfillment is manual and can be executed by anyone (for free) after the given delay /// @param refundAddress The address receiving the left-over fees after the fulfillment /// @param fee The amount to set aside to pay for the fulfillment /// @param gasLimit The gas limit to use for the fulfillment /// @param salt A string being mixed with the randomness seed to obtain different random words /// @param numWords The number of random words requested (from 1 to MAX_RANDOM_WORDS) /// @param delay The number of blocks until the request can be fulfilled (between MIN_DELAY_BLOCKS and MAX_DELAY_BLOCKS) /// @return requestId The id of the request requestLocalVRFRandomWords /// @custom:selector 9478430c function requestLocalVRFRandomWords( address refundAddress, uint256 fee, uint64 gasLimit, bytes32 salt, uint8 numWords, uint64 delay ) external returns (uint256); /// @notice Request random words generated from the relaychain Babe consensus /// @dev The random words are generated from the hash of the all the VRF provided by the /// @dev relaychain validator during 1 epoch. /// @dev It requires a delay of at least 1 epoch after the current epoch to be unpredictable /// @dev at the time the request is performed. /// @dev Warning: /// @dev The validator (on the relaychain) of the last block of an epoch can decide to skip /// @dev producing the block in order to choose the previous generated epoch random number /// @dev at the cost of a relaychain block rewards. It is therefore economically viable to use /// @dev this randomness source only if the financial reward at stake is lower than the relaychain /// @dev block reward. /// @dev (see https://crates.parity.io/pallet_babe/struct.RandomnessFromOneEpochAgo.html) /// @dev Fulfillment is manual and can be executed by anyone (for free) at /// @dev the beginning of the 2nd relay epoch following the current one /// @param refundAddress The address receiving the left-over fees after the fulfillment /// @param fee Amount to set aside to pay for the fulfillment. Those fees are taken from the contract /// @param gasLimit Gas limit for the fulfillment /// @param salt Salt to be mixed with raw randomness to get output /// @param numWords Number of random words to be returned (limited to MAX_RANDOM_WORDS) /// @return requestId The id of the request /// @custom:selector 33c14a63 function requestRelayBabeEpochRandomWords( address refundAddress, uint256 fee, uint64 gasLimit, bytes32 salt, uint8 numWords ) external returns (uint256); /// @dev fulFill the request which will call the contract method "fulfillRandomWords" /// @dev Fees of the caller are refunded if the request is fulfillable /// @param requestId Request to be fulfilled (must be < 2**64) /// @custom:selector 9a91eb0d function fulfillRequest(uint256 requestId) external; /// @param requestId Request receiving the additional fees (must be < 2**64) /// @param feeIncrease Amount to increase /// @custom:selector d0408a7f function increaseRequestFee(uint256 requestId, uint256 feeIncrease) external; /// @param requestId Request to be purged (must be < 2**64) /// @custom:selector 1d26cbab function purgeExpiredRequest(uint256 requestId) external; } ``` The interface includes functions, constants, events, and enums, as covered in the following sections. ### Functions {: #functions } The interface includes the following functions: ??? function "**relayEpochIndex**() - returns the current relay epoch index, where an epoch represents real time and not a block number" === "Parameters" None. === "Returns" - `uint256` current relay epoch index ??? function "**requiredDeposit**() - returns the deposit required to perform a randomness request" === "Parameters" None. === "Returns" - `uint256` required deposit amount ??? function "**getRequestStatus**(*uint256* requestId) - returns the request status of a given randomness request" === "Parameters" - `requestId` - uint256 ID of the randomness request === "Returns" - `uint8` status code of the request ??? function "**getRequest**(*uint256* requestId) - returns the request details of a given randomness request" === "Parameters" - `requestId` - uint256 ID of the randomness request === "Returns" - `bool` whether the request is ready or not - `bool` whether the request is expired or not - `uint256` deposit amount - `uint256` fee amount ??? function "**requestLocalVRFRandomWords**(*address* refundAddress, *uint256* fee, *uint64* gasLimit, *bytes32* salt, *uint8* numWords, *uint64* delay) - request random words generated from the parachain VRF" === "Parameters" - `refundAddress` - address receiving the left-over fees after the fulfillment - `fee` - uint256 amount to set aside to pay for the fulfillment - `gasLimit` - uint64 gas limit to use for the fulfillment - `salt` - bytes32 string that is mixed with the randomness seed to obtain different random words - `numWords` - uint8 number of random words requested, up to the maximum number of random words - `delay` - uint64 number of blocks that must pass before the request can be fulfilled. This value will need to be between the minimum and maximum number of blocks before a local VRF request can be fulfilled === "Returns" - `uint256` ID of the created request ??? function "**requestRelayBabeEpochRandomWords**(*address* refundAddress, *uint256* fee, *uint64* gasLimit, *bytes32* salt, *uint8* numWords) - request random words generated from the relay chain BABE consensus" === "Parameters" - `refundAddress` - address receiving the left-over fees after the fulfillment - `fee` - uint256 amount to set aside to pay for the fulfillment - `gasLimit` - uint64 gas limit to use for the fulfillment - `salt` - bytes32 string that is mixed with the randomness seed to obtain different random words - `numWords` - uint8 number of random words requested, up to the maximum number of random words === "Returns" - `uint256` ID of the created request ??? function "**fulfillRequest**(*uint256* requestId) - fulfill the request which will call the consumer contract method [`fulfillRandomWords`](#:~:text=rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords)). Fees of the caller are refunded if the request is fulfillable" === "Parameters" - `requestId` - uint256 ID of the randomness request === "Returns" None. ??? function "**increaseRequestFee**(*uint256* requestId, *uint256* feeIncrease) - increases the fee associated with a given randomness request. This is needed if the gas price increases significantly before the request is fulfilled" === "Parameters" - `requestId` - uint256 ID of the randomness request - `feeIncrease` - uint256 amount to increase fees by === "Returns" None. ??? function "**purgeExpiredRequest**(*uint256* requestId) - removes a given expired request from storage and transfers the request fees to the caller and the deposit back to the original requester" === "Parameters" - `requestId` - uint256 ID of the randomness request === "Returns" None. ### Constants {: #constants } The interface includes the following constants: - **maxRandomWords** - the maximum number of random words being requested - **minBlockDelay** - the minimum number of blocks before a request can be fulfilled for local VRF requests - **maxBlockDelay** - the maximum number of blocks before a request can be fulfilled for local VRF requests - **deposit** - the deposit amount needed to request random words. There is one deposit per request === "Moonbeam" | Variable | Value | |:----------------------:|:---------------------------------------------------------------:| | MAX_RANDOM_WORDS | {{ networks.moonbeam.randomness.max_random_words }} words | | MIN_VRF_BLOCKS_DELAY | {{ networks.moonbeam.randomness.min_vrf_blocks_delay }} blocks | | MAX_VRF_BLOCKS_DELAY | {{ networks.moonbeam.randomness.max_vrf_blocks_delay }} blocks | | REQUEST_DEPOSIT_AMOUNT | {{ networks.moonbeam.randomness.req_deposit_amount.glmr }} GLMR | === "Moonriver" | Variable | Value | |:----------------------:|:----------------------------------------------------------------:| | MAX_RANDOM_WORDS | {{ networks.moonriver.randomness.max_random_words }} words | | MIN_VRF_BLOCKS_DELAY | {{ networks.moonriver.randomness.min_vrf_blocks_delay }} blocks | | MAX_VRF_BLOCKS_DELAY | {{ networks.moonriver.randomness.max_vrf_blocks_delay }} blocks | | REQUEST_DEPOSIT_AMOUNT | {{ networks.moonriver.randomness.req_deposit_amount.movr }} MOVR | === "Moonbase Alpha" | Variable | Value | |:----------------------:|:--------------------------------------------------------------:| | MAX_RANDOM_WORDS | {{ networks.moonbase.randomness.max_random_words }} words | | MIN_VRF_BLOCKS_DELAY | {{ networks.moonbase.randomness.min_vrf_blocks_delay }} blocks | | MAX_VRF_BLOCKS_DELAY | {{ networks.moonbase.randomness.max_vrf_blocks_delay }} blocks | | REQUEST_DEPOSIT_AMOUNT | {{ networks.moonbase.randomness.req_deposit_amount.dev }} DEV | ### Events {: #events } The interface includes the following events: - **FulfillmentSucceeded**() - emitted when the request has been successfully executed - **FulfillmentFailed**() - emitted when the request has failed to execute fulfillment ### Enums {: #enums } The interface includes the following enums: - **RequestStatus** - the status of the request, which can be `DoesNotExist` (0), `Pending` (1), `Ready` (2), or `Expired` (3) - **RandomnessSource** - the type of the randomness source, which can be `LocalVRF` (0) or `RelayBabeEpoch` (1) ## The Randomness Consumer Solidity Interface {: #randomness-consumer-solidity-interface } The [`RandomnessConsumer.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/4e2a5785424be6faa01cd14e90155d9d2ec734ee/precompiles/randomness/RandomnessConsumer.sol){target=\_blank} Solidity interface makes it easy for smart contracts to interact with the randomness precompile. Using the randomness consumer ensures the fulfillment comes from the randomness precompile. ??? code "RandomnessConsumer.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The Randomness contract's address. address constant RANDOMNESS_ADDRESS = 0x0000000000000000000000000000000000000809; /// @dev The Randomness contract's instance. Randomness constant RANDOMNESS_CONTRACT = Randomness(RANDOMNESS_ADDRESS); /// @dev Maximum number of random words being requested uint32 constant MAX_RANDOM_WORDS = 100; /// @dev Minimum number of blocks before a request can be fulfilled for Local VRF Request uint32 constant MIN_VRF_BLOCKS_DELAY = 2; /// @dev Maximum number of blocks before a request can be fulfilled for Local VRF Request uint32 constant MAX_VRF_BLOCKS_DELAY = 2000; /// @dev The deposit amount needed to request random words. There is 1 deposit per request uint256 constant REQUEST_DEPOSIT_AMOUNT = 1000000000000000000; /// @author The Moonbeam Team /// @title Pallet Randomness Interface /// @dev The interface through which solidity contracts will interact with Randomness /// @custom:address 0x0000000000000000000000000000000000000809 interface Randomness { /// @notice Event emitted when the request has been successfully executed event FulFillmentSucceeded(); /// @notice Event emitted when the request has failed to execute fulfillment event FulFillmentFailed(); /// @notice The status of the request /// @param DoesNotExist The request doesn't exist /// @param Pending The request cannot be fulfilled yet /// @param Ready The request is ready to be fulfilled /// @param Expired The request has expired enum RequestStatus { DoesNotExist, Pending, Ready, Expired } /// @notice The type of randomness source /// @param LocalVRF Randomness VRF using the parachain material as seed /// @param RelayBabeEpoch Randomness VRF using relay material from previous epoch enum RandomnessSource { LocalVRF, RelayBabeEpoch } /// @notice The request details /// @param id The id of the request (is always < 2**64) /// @param refundAddress The address receiving the left-over fees after the fulfillment /// @param contractAddress The address of the contract being called back during fulfillment /// @param fee The amount to set aside to pay for the fulfillment /// @param gasLimit The gas limit to use for the fulfillment /// @param salt A string being mixed with the randomness seed to obtain different random words. This should be as unique as possible; using the same salt will lead to same randomness result. /// @param numWords The number of random words requested (from 1 to MAX_RANDOM_WORDS) /// @param randomnessSource The type of randomness source used to generate the random words /// @param fulfillmentBlock The parachain block number at which the request can be fulfilled (for LocalVRF only) /// @param fulfillmentEpochIndex The relay epoch index at which the request can be fulfilled (for RelayBabeEpoch) /// @param expirationBlock The parachain block number at which the request expires (for LocalVRF only) /// @param expirationEpochIndex The relay epoch index at which the request expires (for RelayBabeEpoch) /// @param status The current status of the request struct Request { uint256 id; address refundAddress; address contractAddress; uint256 fee; uint256 gasLimit; bytes32 salt; uint32 numWords; RandomnessSource randomnessSource; uint32 fulfillmentBlock; uint64 fulfillmentEpochIndex; uint32 expirationBlock; uint64 expirationEpochIndex; RequestStatus status; } /// Return the current relay epoch index /// @dev An epoch represents real time and not a block number /// @dev Currently, time between epoch changes cannot be longer than: /// @dev - Kusama/Westend/Rococo: 600 relay blocks (1 hour) /// @dev - Polkadot: 2400 relay blocks (4 hours) /// @custom:selector 81797566 function relayEpochIndex() external view returns (uint64); /// Return the deposit required to perform a request /// @dev Each request will need a deposit. /// @custom:selector fb7cfdd7 function requiredDeposit() external view returns (uint256); /// @notice Returns the request status /// @param requestId The id of the request to check (must be < 2**64) /// @return status Status of the request /// @custom:selector d8a4676f function getRequestStatus(uint256 requestId) external view returns (RequestStatus status); /// @notice Returns the request or revert /// @param requestId The id of the request to check (must be < 2**64) /// @return request The request /// @custom:selector c58343ef function getRequest(uint256 requestId) external view returns (Request memory request); /// @notice Request random words generated from the parachain VRF /// @dev This is using pseudo-random VRF executed by the collator at the fulfillment /// @dev Warning: /// @dev The collator in charge of producing the block at fulfillment can decide to skip /// @dev producing the block in order to have a different random word generated by the next /// @dev collator, at the cost of a block reward. It is therefore economically viable to use /// @dev this randomness source only if the financial reward at stake is lower than the block /// @dev reward. /// @dev In order to reduce the risk of a collator being able to predict the random words /// @dev when the request is performed, it is possible to increase the delay to multiple blocks /// @dev The higher the delay is, the less likely the collator will be able to know which /// @dev collator will be in charge of fulfilling the request. /// @dev Fulfillment is manual and can be executed by anyone (for free) after the given delay /// @param refundAddress The address receiving the left-over fees after the fulfillment /// @param fee The amount to set aside to pay for the fulfillment /// @param gasLimit The gas limit to use for the fulfillment /// @param salt A string being mixed with the randomness seed to obtain different random words /// @param numWords The number of random words requested (from 1 to MAX_RANDOM_WORDS) /// @param delay The number of blocks until the request can be fulfilled (between MIN_DELAY_BLOCKS and MAX_DELAY_BLOCKS) /// @return requestId The id of the request requestLocalVRFRandomWords /// @custom:selector 9478430c function requestLocalVRFRandomWords( address refundAddress, uint256 fee, uint64 gasLimit, bytes32 salt, uint8 numWords, uint64 delay ) external returns (uint256); /// @notice Request random words generated from the relaychain Babe consensus /// @dev The random words are generated from the hash of the all the VRF provided by the /// @dev relaychain validator during 1 epoch. /// @dev It requires a delay of at least 1 epoch after the current epoch to be unpredictable /// @dev at the time the request is performed. /// @dev Warning: /// @dev The validator (on the relaychain) of the last block of an epoch can decide to skip /// @dev producing the block in order to choose the previous generated epoch random number /// @dev at the cost of a relaychain block rewards. It is therefore economically viable to use /// @dev this randomness source only if the financial reward at stake is lower than the relaychain /// @dev block reward. /// @dev (see https://crates.parity.io/pallet_babe/struct.RandomnessFromOneEpochAgo.html) /// @dev Fulfillment is manual and can be executed by anyone (for free) at /// @dev the beginning of the 2nd relay epoch following the current one /// @param refundAddress The address receiving the left-over fees after the fulfillment /// @param fee Amount to set aside to pay for the fulfillment. Those fees are taken from the contract /// @param gasLimit Gas limit for the fulfillment /// @param salt Salt to be mixed with raw randomness to get output /// @param numWords Number of random words to be returned (limited to MAX_RANDOM_WORDS) /// @return requestId The id of the request /// @custom:selector 33c14a63 function requestRelayBabeEpochRandomWords( address refundAddress, uint256 fee, uint64 gasLimit, bytes32 salt, uint8 numWords ) external returns (uint256); /// @dev fulFill the request which will call the contract method "fulfillRandomWords" /// @dev Fees of the caller are refunded if the request is fulfillable /// @param requestId Request to be fulfilled (must be < 2**64) /// @custom:selector 9a91eb0d function fulfillRequest(uint256 requestId) external; /// @param requestId Request receiving the additional fees (must be < 2**64) /// @param feeIncrease Amount to increase /// @custom:selector d0408a7f function increaseRequestFee(uint256 requestId, uint256 feeIncrease) external; /// @param requestId Request to be purged (must be < 2**64) /// @custom:selector 1d26cbab function purgeExpiredRequest(uint256 requestId) external; } ``` The consumer interface includes the following functions: - **fulfillRandomWords**(*uint256* requestId, *uint256[] memory* randomWords) - handles the VRF response for a given request. This method is triggered by a call to `rawFulfillRandomWords` - **rawFulfillRandomWords**(*uint256* requestId, *uint256[] memory* randomWords) - executed when the [`fulfillRequest` function](#:~:text=fulfillRequest(uint256 requestId)) of the randomness precompile is called. The origin of the call is validated, ensuring the randomness precompile is the origin, and then the `fulfillRandomWords` method is called ## Request & Fulfill Process {: #request-and-fulfill-process } To consume randomness, you must have a contract that does the following: - Imports the `Randomness.sol` precompile and `RandomnessConsumer.sol` interface - Inherits from the `RandomnessConsumer.sol` interface - Requests randomness through the precompile's [`requestLocalVRFRandomWords` method](#:~:text=requestLocalVRFRandomWords) or [`requestRelayBabeEpochRandomWords` method](#:~:text=requestRelayBabeEpochRandomWords), depending on the source of randomness you want to use - Requests fulfillment through the precompile's [`fulfillRequest` method](#:~:text=fulfillRequest) - Consumes randomness through a `fulfillRandomWords` method with the same [signature as the `fulfillRandomWords` method](#:~:text=fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)) of the `RandomnessConsumer.sol` contract When randomness is requested through the precompile's `requestLocalVRFRandomWords` or `requestRelayBabeEpochRandomWords` method, a fee is set aside to pay for the fulfillment of the request. When using local VRF, to increase unpredictability, a specified delay period (in blocks) must pass before the request can be fulfilled. At the very least, the delay period must be greater than one block. For BABE epoch randomness, you do not need to specify a delay but can fulfill the request at the beginning of the 2nd epoch following the current one. After the delay, fulfillment of the request can be manually executed by anyone through the `fulfillRequest` method using the fee that was initially set aside for the request. When fulfilling the randomness request via the precompile's `fulfillRequest` method, the [`rawFulfillRandomWords`](#:~:text=rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords)) function in the `RandomnessConsumer.sol` contract will be called, which will verify that the sender is the randomness precompile. From there, [`fulfillRandomWords`](#:~:text=fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)) is called and the requested number of random words are computed using the current block's randomness result and a given salt and returned. If the fulfillment was successful, the [`FulfillmentSucceeded` event](#:~:text=FulfillmentSucceeded) will be emitted; otherwise the [`FulfillmentFailed` event](#:~:text=FulfillmentFailed) will be emitted. For fulfilled requests, the cost of execution will be refunded from the request fee to the caller of `fulfillRequest`. Then any excess fees and the request deposit are transferred to the specified refund address. Your contract's `fulfillRandomWords` callback is responsible for handling the fulfillment. For example, in a lottery contract, the callback would use the random words to choose a winner and payout the winnings. If a request expires it can be purged through the precompile's [`purgeExpiredRequest` function](/builders/ethereum/precompiles/features/randomness/#:~:text=purgeExpiredRequest){target=\_blank}. When this function is called the request fee is paid out to the caller and the deposit will be returned to the original requester. The happy path for a randomness request is shown in the following diagram: ![Randomness request happy path diagram](/images/learn/features/randomness/randomness-1.webp) ## Generate a Random Number using the Randomness Precompile {: #interact-with-the-solidity-interfaces } In the following sections of this tutorial, you'll learn how to create a smart contract that generates a random number using the Randomness Precompile and the Randomness Consumer. If you want to just explore some of the functions of the Randomness Precompile, you can skip ahead to the [Use Remix to Interact Directly with the Randomness Precompile](#interact-directly) section. ### Checking Prerequisites {: #checking-prerequisites } For this guide, you will need to have the following: - [MetaMask installed and connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - An account funded with DEV tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Create a Random Number Generator Contract {: #create-random-generator-contract } The contract that will be created in this section includes the functions that you'll need at a bare minimum to request randomness and consume the results from fulfilling randomness requests. **This contract is for educational purposes only and is not meant for production use.** The contract will include the following functions: - A constructor that accepts the deposit required to request randomness - A function that submits randomness requests. For this example, the source of randomness will be local VRF, but you can easily modify the contract to use BABE epoch randomness - A function that fulfills the request by calling the `fulfillRequest` function of the Randomness Precompile. This function will be `payable` as the fulfillment fee will need to be submitted at the time of the randomness request - A function that consumes the fulfillment results. This function's signature must match the [signature of the `fulfillRandomWords` method](#:~:text=fulfillRandomWords(uint256 requestId, uint256[] memory randomWords)) of the Randomness Consumer contract Without further ado, the contract is as follows: ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/randomness/Randomness.sol"; import {RandomnessConsumer} from "https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/randomness/RandomnessConsumer.sol"; contract RandomNumber is RandomnessConsumer { // The Randomness Precompile Interface Randomness public randomness = Randomness(0x0000000000000000000000000000000000000809); // Variables required for randomness requests uint256 public requiredDeposit = randomness.requiredDeposit(); uint64 public FULFILLMENT_GAS_LIMIT = 100000; // The fee can be set to any value as long as it is enough to cover // the fulfillment costs. Any leftover fees will be refunded to the // refund address specified in the requestRandomness function below. // 150 Gwei should be sufficient for all networks. // For Moonbase Alpha and Moonriver, you can specify 5 Gwei uint256 public MIN_FEE = FULFILLMENT_GAS_LIMIT * 150 gwei; uint32 public VRF_BLOCKS_DELAY = MIN_VRF_BLOCKS_DELAY; bytes32 public SALT_PREFIX = "INSERT_ANY_STRING_FOR_SALT"; // Storage variables for the current request uint256 public requestId; uint256[] public random; constructor() payable RandomnessConsumer() { // Because this contract can only perform 1 random request at a time, // We only need to have 1 required deposit require(msg.value >= requiredDeposit); } function requestRandomness() public payable { // Make sure that the value sent is enough require(msg.value >= MIN_FEE); // Request local VRF randomness requestId = randomness.requestLocalVRFRandomWords( msg.sender, // Refund address msg.value, // Fulfillment fee FULFILLMENT_GAS_LIMIT, // Gas limit for the fulfillment SALT_PREFIX ^ bytes32(requestId++), // A salt to generate unique results 1, // Number of random words VRF_BLOCKS_DELAY // Delay before request can be fulfilled ); } function fulfillRequest() public { randomness.fulfillRequest(requestId); } function fulfillRandomWords( uint256, // requestId uint256[] memory randomWords ) internal override { // Save the randomness results random = randomWords; } } ``` As you can see, there are also some constants in the contract that can be edited as you see fit, especially the `SALT_PREFIX` which can be used to produce unique results. In the following sections, you'll use Remix to deploy and interact with the contract. ### Remix Set Up {: #remix-set-up} To add the contract to Remix and follow along with this section of the tutorial, you will need to create a new file named `RandomnessNumber.sol` in Remix and paste the `RandomNumber` contract into the file. ![Add the random number generator contract to Remix.](/images/builders/ethereum/precompiles/features/randomness/randomness-2.webp) ### Compile & Deploy the Random Number Generator Contract {: #compile-deploy-random-number } To compile the `RandomNumber.sol` contract in Remix, you'll need to take the following steps: 1. Click on the **Compile** tab, second from top 2. Click on the **Compile RandomNumber.sol** button If the contract was compiled successfully, you will see a green checkmark next to the **Compile** tab. ![Compile the random number generator contract in Remix.](/images/builders/ethereum/precompiles/features/randomness/randomness-3.webp) Now you can go ahead and deploy the contract by taking these steps: 1. Click on the **Deploy and Run** tab directly below the **Compile** tab 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** dropdown. Once you select **Injected Provider - Metamask**, you might be prompted by MetaMask to connect your account to Remix 3. Make sure the correct account is displayed under **ACCOUNT** 4. Enter the deposit amount in the **VALUE** field, which is `{{ networks.moonbase.randomness.req_deposit_amount.wei }}` in Wei (`{{ networks.moonbase.randomness.req_deposit_amount.dev }}` Ether) 5. Ensure **RandomNumber - RandomNumber.sol** is selected in the **CONTRACT** dropdown 6. Click **Deploy** 7. Confirm the MetaMask transaction that appears by clicking **Confirm** ![Deploy the random number generator contract in Remix.](/images/builders/ethereum/precompiles/features/randomness/randomness-4.webp) The **RANDOMNUMBER** contract will appear in the list of **Deployed Contracts**. ### Submit a Request to Generate a Random Number {: #request-randomness } To request randomness, you're going to use the `requestRandomness` function of the contract, which will require you to submit a deposit as defined in the Randomness Precompile. You can submit the randomness request and pay the deposit by taking these steps: 1. Enter an amount in the **VALUE** field for the fulfillment fee, it must be equal to or greater than the minimum fee specified in the `RandomNumber` contract, which is `15000000` Gwei. 2. Expand the **RANDOMNUMBER** contract 3. Click on the **requestRandomness** button 4. Confrm the transaction in MetaMask ![Request a random number using the random number generator contract in Remix.](/images/builders/ethereum/precompiles/features/randomness/randomness-5.webp) Once you submit the transaction, the `requestId` will be updated with the ID of the request. You can use the `requestId` call of the Random Number contract to get the request ID and the `getRequestStatus` function of the Randomness Precompile to check the status of this request ID. ### Fulfill the Request and Save the Random Number {: #fulfill-request-save-number } After submitting the randomness request, you'll need to wait for the duration of the delay before you can fulfill the request. For the `RandomNumber.sol` contract, the delay was set to the minimum block delay defined in the Randomness Precompile, which is {{ networks.moonbase.randomness.min_vrf_blocks_delay }} blocks. You must also fulfill the request before it is too late. For local VRF, the request expires after {{ networks.moonbase.randomness.block_expiration }} blocks and for BABE epoch randomness, the request expires after {{ networks.moonbase.randomness.epoch_expiration }} epochs. Assuming you've waited for the minimum blocks (or epochs if you're using BABE epoch randomness) to pass and the request hasn't expired, you can fulfill the request by taking the following steps: 1. Click on the **fulfillRequest** button 2. Confirming the transaction in MetaMask ![Fulfill the randomness request using the random number generator contract in Remix.](/images/builders/ethereum/precompiles/features/randomness/randomness-6.webp) Once the request has been fulfilled, you can check the random number that was generated: 1. Expand the **random** function 2. Since the contract only requested one random word, you can get the random number by accessing the `0` index of the `random` array 3. Click **call** 4. The random number will appear below the **call** button ![Retrieve the random number that was generated by the random number contract in Remix.](/images/builders/ethereum/precompiles/features/randomness/randomness-7.webp) Upon successful fulfillment, the excess fees and deposit will be sent to the address specified as the refund address. If the request happened to expire before it could be fulfilled, you can interact with the Randomness Precompile directly to purge the request and unlock the deposit and fees. Please refer to the following section for instructions on how to do this. ## Use Remix to Interact Directly with the Randomness Precompile {: #interact-directly } In addition to interacting with the randomness precompile via a smart contract, you can also interact with it directly in Remix to perform operations such as creating a randomness request, checking on the status of a request, and purging expired requests. Remember, you need to have a contract that inherits from the consumer contract in order to fulfill requests, as such if you fulfill a request using the precompile directly there will be no way to consume the results. ### Remix Set Up {: #remix-set-up } To add the interfaces to Remix and follow along with this section of the tutorial, you will need to: 1. Get a copy of [`Randomness.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/randomness/Randomness.sol){target=\_blank} 2. Paste the file contents into a Remix file named **Randomness.sol** ![Add precompile to Remix](/images/builders/ethereum/precompiles/features/randomness/randomness-8.webp) ### Compile & Access the Randomness Precompile {: #compile-randomness } Next, you will need to compile the `Randomness.sol` file in Remix. To get started, make sure you have the **Randomness.sol** file open and take the following steps: 1. Click on the **Compile** tab, second from top 2. To compile the contract, click on **Compile Randomness.sol** If the contract was compiled successfully, you will see a green checkmark next to the **Compile** tab. Instead of deploying the randomness precompile, you will access the interface given the address of the precompiled contract: 1. Click on the **Deploy and Run** tab directly below the **Compile** tab in Remix. Please note the precompiled contract is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** dropdown. Once selected, you might be prompted by MetaMask to connect your account to Remix 3. Make sure the correct account is displayed under **ACCOUNT** 4. Ensure **Randomness - Randomness.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract, there is no need to deploy any code. Instead we are going to provide the address of the precompile in the **At Address** Field 5. Provide the address of the batch precompile: `{{ networks.moonbase.precompiles.randomness }}` and click **At Address** ![Access the address](/images/builders/ethereum/precompiles/features/randomness/randomness-9.webp) The **RANDOMNESS** precompile will appear in the list of **Deployed Contracts**. You will use this to fulfill the randomness request made from the lottery contract later on in this tutorial. ### Get Request Status & Purge Expired Request {: #get-request-status-and-purge } Anyone can purge expired requests. You do not need to be the one who requested the randomness to be able to purge it. When you purge an expired request, the request fees will be transferred to you, and the deposit for the request will be returned to the original requester. To purge a request, first you have to make sure that the request has expired. To do so, you can verify the status of a request using the `getRequestStatus` function of the precompile. The number that is returned from this call corresponds to the index of the value in the [`RequestStatus`](#enums) enum. As a result, you'll want to verify the number returned is `3` for `Expired`. Once you've verified that the request is expired, you can call the `purgeExpiredRequest` function to purge the request. To verify and purge a request, you can take the following steps: 1. Expand the **RANDOMNESS** contract 2. Enter the request ID of the request you want to verify has expired and click on **getRequestStatus** 3. The response will appear just underneath the function. Verify that you received a `3` 4. Expand the **purgeExpiredRequest** function and enter the request ID 5. Click on **transact** 6. MetaMask will pop-up and you can confirm the transaction ![Purge an exired request](/images/builders/ethereum/precompiles/features/randomness/randomness-10.webp) Once the transaction goes through, you can verify the request has been purged by calling the **getRequestStatus** function again with the same request ID. You should receive a status of `0`, or `DoesNotExist`. You can also expect the amount of the request fees to be transferred to your account. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/features/staking/ --- BEGIN CONTENT --- --- title: Staking Precompile Contract description: Unlock the potential of staking with a specialized precompiled contract designed to streamline and optimize participation in Moonbeam. keywords: solidity, ethereum, staking, moonbeam, precompiled, contracts categories: Precompiles, Ethereum Toolkit --- # Interacting with the Staking Precompile ## Introduction {: #introduction } Moonbeam uses a Delegated Proof of Stake system through the [Parachain Staking](/builders/substrate/interfaces/features/staking/){target=\_blank} Pallet, allowing token holders (delegators) to express exactly which collator candidates they would like to support and with what quantity of stake. The design of the Parachain Staking Pallet is such that it enforces shared risk/reward on chain between delegators and candidates. For general information on staking, such as general terminology, staking variables, and more, please refer to the [Staking on Moonbeam](/learn/features/staking/){target=\_blank} page. The staking module is coded in Rust and it is part of a pallet that is normally not accessible from the Ethereum side of Moonbeam. However, a staking precompile allows developers to access the staking features using the Ethereum API in a precompiled contract located at address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.staking}} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.staking}} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.staking}} ``` This guide will cover the available methods in the staking precompile interface. In addition, it will show you how to interact with the Parachain Staking Pallet through the staking precompile and the Ethereum API. The examples in this guide are done on Moonbase Alpha, but they can be adapted for Moonbeam or Moonriver. !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## Exit Delays {: #exit-delays } Some of the Parachain Staking Pallet extrinsics include exit delays that you must wait before the request can be executed. The exit delays to note are as follows: === "Moonbeam" | Variable | Value | |:-----------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------:| | Decrease candidate bond | {{ networks.moonbeam.collator_timings.can_bond_less.rounds }} rounds ({{ networks.moonbeam.collator_timings.can_bond_less.hours }} hours) | | Decrease delegator bond | {{ networks.moonbeam.delegator_timings.del_bond_less.rounds }} rounds ({{ networks.moonbeam.delegator_timings.del_bond_less.hours }} hours) | | Revoke delegation | {{ networks.moonbeam.delegator_timings.revoke_delegations.rounds }} rounds ({{ networks.moonbeam.delegator_timings.revoke_delegations.hours }} hours) | | Leave candidates | {{ networks.moonbeam.collator_timings.leave_candidates.rounds }} rounds ({{ networks.moonbeam.collator_timings.leave_candidates.hours }} hours) | | Leave delegators | {{ networks.moonbeam.delegator_timings.leave_delegators.rounds }} rounds ({{ networks.moonbeam.delegator_timings.leave_delegators.hours }} hours) | === "Moonriver" | Variable | Value | |:-----------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------:| | Decrease candidate bond | {{ networks.moonriver.collator_timings.can_bond_less.rounds }} rounds ({{ networks.moonriver.collator_timings.can_bond_less.hours }} hours) | | Decrease delegator bond | {{ networks.moonriver.delegator_timings.del_bond_less.rounds }} rounds ({{ networks.moonriver.delegator_timings.del_bond_less.hours }} hours) | | Revoke delegation | {{ networks.moonriver.delegator_timings.revoke_delegations.rounds }} rounds ({{ networks.moonriver.delegator_timings.revoke_delegations.hours }} hours) | | Leave candidates | {{ networks.moonriver.collator_timings.leave_candidates.rounds }} rounds ({{ networks.moonriver.collator_timings.leave_candidates.hours }} hours) | | Leave delegators | {{ networks.moonriver.delegator_timings.leave_delegators.rounds }} rounds ({{ networks.moonriver.delegator_timings.leave_delegators.hours }} hours) | === "Moonbase Alpha" | Variable | Value | |:-----------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------:| | Decrease candidate bond | {{ networks.moonbase.collator_timings.can_bond_less.rounds }} rounds ({{ networks.moonbase.collator_timings.can_bond_less.hours }} hours) | | Decrease delegator bond | {{ networks.moonbase.delegator_timings.del_bond_less.rounds }} rounds ({{ networks.moonbase.delegator_timings.del_bond_less.hours }} hours) | | Revoke delegation | {{ networks.moonbase.delegator_timings.revoke_delegations.rounds }} rounds ({{ networks.moonbase.delegator_timings.revoke_delegations.hours }} hours) | | Leave candidates | {{ networks.moonbase.collator_timings.leave_candidates.rounds }} rounds ({{ networks.moonbase.collator_timings.leave_candidates.hours }} hours) | | Leave delegators | {{ networks.moonbase.delegator_timings.leave_delegators.rounds }} rounds ({{ networks.moonbase.delegator_timings.leave_delegators.hours }} hours) | ## Parachain Staking Solidity Interface {: #the-parachain-staking-solidity-interface } [`StakingInterface.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/parachain-staking/StakingInterface.sol){target=\_blank} is an interface through which Solidity contracts can interact with parachain-staking. The beauty is that Solidity developers don’t have to learn the Substrate API. Instead, they can interact with staking functions using the Ethereum interface they are familiar with. The Solidity interface includes the following functions: ??? function "**isDelegator**(*address* delegator) - read-only function that checks whether the specified address is currently a staking delegator. Uses the [`delegatorState`](/builders/substrate/interfaces/features/staking/#:~:text=delegatorState(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `delegator` - address to check if they are currently a delegator === "Returns" - `bool` whether the address is currently a delegator ??? function "**isCandidate**(*address* candidate) - read-only function that checks whether the specified address is currently a collator candidate. Uses the [`candidateState`](/builders/substrate/interfaces/features/staking/#:~:text=candidateState(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address to check if they are currently a collator candidate === "Returns" - `bool` whether the address is currently a candidate ??? function "**isSelectedCandidate**(*address* candidate) - read-only function that checks whether the specified address is currently part of the active collator set. Uses the [`selectedCandidates`](/builders/substrate/interfaces/features/staking/#:~:text=selectedCandidates()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address to check if they are currently an active collator === "Returns" - `bool` whether the address is currently an active collator ??? function "**points**(*uint256* round) - read-only function that gets the total points awarded to all collators in a given round. Uses the [`points`](/builders/substrate/interfaces/features/staking/#:~:text=points(u32)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `round` - uint256 round number to query points for === "Returns" - `uint256` total points awarded in the specified round ??? function "**awardedPoints**(*uint32* round, *address* candidate) - read-only function that returns the total points awarded in a given round to a given collator. If `0` is returned, it could be because no blocks were produced or the storage for that round has been removed. Uses the [`points`](/builders/substrate/interfaces/features/staking/#:~:text=awardedPts(u32, AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `round` - uint32 round number to query - `candidate` - address of the collator to query points for === "Returns" - `uint256` points awarded to the collator in the specified round ??? function "**delegationAmount**(*address* delegator, *address* candidate) - read-only function that returns the amount delegated by a given delegator in support of a given candidate. Uses the [`delegatorState`](/builders/substrate/interfaces/features/staking/#:~:text=delegatorState(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `delegator` - address of the delegator - `candidate` - address of the candidate === "Returns" - `uint256` amount delegated ??? function "**isInTopDelegations**(*address* delegator, *address* candidate) - read-only function that returns a boolean indicating whether the given delegator is in the top delegations for the given candidate. Uses the [`topDelegations`](/builders/substrate/interfaces/features/staking/#:~:text=topDelegations(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `delegator` - address of the delegator to check - `candidate` - address of the candidate === "Returns" - `bool` whether the delegator is in the top delegations ??? function "**minDelegation**() - read-only function that gets the minimum delegation amount. Uses the [`minDelegation`](/builders/substrate/interfaces/features/staking/#:~:text=minDelegation()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" None. === "Returns" - `uint256` minimum delegation amount ??? function "**candidateCount**() - read-only function that gets the current amount of collator candidates. Uses the [`candidatePool`](/builders/substrate/interfaces/features/staking/#:~:text=candidatePool()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" None. === "Returns" - `uint256` current number of collator candidates ??? function "**round**() - read-only function that returns the current round number. Uses the [`round`](/builders/substrate/interfaces/features/staking/#:~:text=round()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" None. === "Returns" - `uint256` current round number ??? function "**candidateDelegationCount**(*address* candidate) - read-only function that returns the number of delegations for the specified collator candidate address. Uses the [`candidateInfo`](/builders/substrate/interfaces/features/staking/#:~:text=candidateInfo(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the collator candidate to query === "Returns" - `uint256` number of delegations for the candidate ??? function "**candidateAutoCompoundingDelegationCount**(*address* candidate) - a read-only function that returns the number of auto-compounding delegations for the specified candidate. Uses the [`autoCompoundingDelegations`](/builders/substrate/interfaces/features/staking/#:~:text=autoCompoundingDelegations(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to query === "Returns" - `uint256` number of auto-compounding delegations ??? function "**delegatorDelegationCount**(*address* delegator) - read-only function that returns the number of delegations for the specified delegator address. Uses the [`delegatorState`](/builders/substrate/interfaces/features/staking/#:~:text=delegatorState(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `delegator` - address of the delegator to query === "Returns" - `uint256` number of delegations for the delegator ??? function "**selectedCandidates**() - read-only function that gets the selected candidates for the current round. Uses the [`selectedCandidates`](/builders/substrate/interfaces/features/staking/#:~:text=selectedCandidates()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" None. === "Returns" - `address[]` array of selected candidate addresses ??? function "**delegationRequestIsPending**(*address* delegator, *address* candidate) - returns a boolean to indicate whether there is a pending delegation request made by a given delegator for a given candidate" === "Parameters" - `delegator` - address of the delegator - `candidate` - address of the candidate === "Returns" - `bool` whether there is a pending delegation request ??? function "**candidateExitIsPending**(*address* candidate) - returns a boolean to indicate whether a pending exit exists for a specific candidate. Uses the [`candidateInfo`](/builders/substrate/interfaces/features/staking/#:~:text=candidateInfo(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to check === "Returns" - `bool` whether there is a pending exit request ??? function "**candidateRequestIsPending**(*address* candidate) - returns a boolean to indicate whether there is a pending bond less request made by a given candidate. Uses the [`candidateInfo`](/builders/substrate/interfaces/features/staking/#:~:text=candidateInfo(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to check === "Returns" - `bool` whether there is a pending bond less request ??? function "**delegationAutoCompound**(*address* delegator, *address* candidate) - returns the auto-compound percentage for a delegation given the delegator and candidate" === "Parameters" - `delegator` - address of the delegator - `candidate` - address of the candidate === "Returns" - `uint256` auto-compound percentage ??? function "**getDelegatorTotalStaked**(*address* delegator) - read-only function that returns the total staked amount of a given delegator, regardless of the candidate. Uses the [`delegatorState`](/builders/substrate/interfaces/features/staking/#:~:text=delegatorState(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `delegator` - address of the delegator to query === "Returns" - `uint256` total staked amount ??? function "**getCandidateTotalCounted**(*address* candidate) - read-only function that returns the total amount staked for a given candidate. Uses the [`candidateInfo`](/builders/substrate/interfaces/features/staking/#:~:text=candidateInfo(AccountId20)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to query === "Returns" - `uint256` total amount staked for the candidate ??? function "**joinCandidates**(*uint256* amount, *uint256* candidateCount) - allows the account to join the set of collator candidates with the specified bond amount and the current candidate count. Uses the [`joinCandidates`](/builders/substrate/interfaces/features/staking/#:~:text=joinCandidates(bond, candidateCount)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `amount` - uint256 bond amount to stake as a candidate - `candidateCount` - uint256 current number of candidates in the pool === "Returns" None. ??? function "**scheduleLeaveCandidates**(*uint256* candidateCount) - schedules a request for a candidate to remove themselves from the candidate pool. Scheduling the request does not automatically execute it. There is an [exit delay](#exit-delays) that must be waited before you can execute the request via the `executeLeaveCandidates` extrinsic. Uses the [`scheduleLeaveCandidates`](/builders/substrate/interfaces/features/staking/#:~:text=scheduleLeaveCandidates(candidateCount)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidateCount` - uint256 current number of candidates in the pool === "Returns" None. ??? function "**executeLeaveCandidates**(*address* candidate, *uint256* candidateDelegationCount) - executes the due request to leave the set of collator candidates. Uses the [`executeLeaveCandidates`](/builders/substrate/interfaces/features/staking/#:~:text=executeLeaveCandidates(candidate, candidateDelegationCount)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate leaving the pool - `candidateDelegationCount` - uint256 number of delegations for the candidate === "Returns" None. ??? function "**cancelLeaveCandidates**(*uint256* candidateCount) - allows a candidate to cancel a pending scheduled request to leave the candidate pool. Given the current number of candidates in the pool. Uses the [`cancelLeaveCandidates`](/builders/substrate/interfaces/features/staking/#:~:text=cancelLeaveCandidates(candidateCount)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidateCount` - uint256 current number of candidates in the pool === "Returns" None. ??? function "**goOffline**() - temporarily leave the set of collator candidates without unbonding. Uses the [`goOffline`](/builders/substrate/interfaces/features/staking/#:~:text=goOffline()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" None. === "Returns" None. ??? function "**goOnline**() - rejoin the set of collator candidates after previously calling `goOffline()`. Uses the [`goOnline`](/builders/substrate/interfaces/features/staking/#:~:text=goOnline()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" None. === "Returns" None. ??? function "**candidateBondMore**(*uint256* more) - collator candidate increases bond by the specified amount. Uses the [`candidateBondMore`](/builders/substrate/interfaces/features/staking/#:~:text=candidateBondMore(more)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `more` - uint256 amount to increase the bond by === "Returns" None. ??? function "**scheduleCandidateBondLess**(*uint256* less) - schedules a request to decrease a candidates bond by the specified amount. Scheduling the request does not automatically execute it. There is an [exit delay](#exit-delays) that must be waited before you can execute the request via the `execute_candidate_bond_request` extrinsic. Uses the [`scheduleCandidateBondLess`](/builders/substrate/interfaces/features/staking/#:~:text=scheduleCandidateBondLess(less)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `less` - uint256 amount to decrease the bond by === "Returns" None. ??? function "**executeCandidateBondLess**(*address* candidate) - executes any due requests to decrease a specified candidate's bond amount. Uses the [`executeCandidateBondLess`](/builders/substrate/interfaces/features/staking/#:~:text=executeCandidateBondLess(candidate)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to execute the bond decrease for === "Returns" None. ??? function "**cancelCandidateBondLess**() - allows a candidate to cancel a pending scheduled request to decrease a candidates bond. Uses the [`cancelCandidateBondLess`](/builders/substrate/interfaces/features/staking/#:~:text=cancelCandidateBondLess()){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" None. === "Returns" None. ??? function "**delegateWithAutoCompound**(*address* candidate, *uint256* amount, *uint8* autoCompound, *uint256* candidateDelegationCount, *uint256* candidateAutoCompoundingDelegationCount, *uint256* delegatorDelegationCount) - makes a delegation in support of a collator candidate and automatically sets the percent of rewards to auto-compound given an integer (no decimals) for `autoCompound` between 0-100. Uses the [`delegateWithAutoCompound`](/builders/substrate/interfaces/features/staking/#:~:text=delegateWithAutoCompound){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to delegate to - `amount` - uint256 amount to delegate - `autoCompound` - uint8 percentage of rewards to auto-compound (0-100) - `candidateDelegationCount` - uint256 current number of delegations for the candidate - `candidateAutoCompoundingDelegationCount` - uint256 current number of auto-compounding delegations for the candidate - `delegatorDelegationCount` - uint256 current number of delegations from the delegator === "Returns" None. ??? function "**scheduleRevokeDelegation**(*address* candidate) - schedules a request to revoke a delegation given the address of a candidate. Scheduling the request does not automatically execute it. There is an [exit delay](#exit-delays) that must be waited before you can execute the request via the `executeDelegationRequest` extrinsic. Uses the [`scheduleRevokeDelegation`](/builders/substrate/interfaces/features/staking/#:~:text=scheduleRevokeDelegation(collator)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to revoke delegation from === "Returns" None. ??? function "**delegatorBondMore**(*address* candidate, *uint256* more) - delegator increases bond to a collator by the specified amount. Uses the [`delegatorBondMore`](/builders/substrate/interfaces/features/staking/#:~:text=delegatorBondMore(candidate, more)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to increase delegation for - `more` - uint256 amount to increase the delegation by === "Returns" None. ??? function "**scheduleDelegatorBondLess**(*address* candidate, *uint256* less) - schedules a request for a delegator to bond less with respect to a specific candidate. Scheduling the request does not automatically execute it. There is an [exit delay](#exit-delays) that must be waited before you can execute the request via the `executeDelegationRequest` extrinsic. Uses the [`scheduleDelegatorBondLess`](/builders/substrate/interfaces/features/staking/#:~:text=scheduleDelegatorBondLess(candidate, less)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to decrease delegation for - `less` - uint256 amount to decrease the delegation by === "Returns" None. ??? function "**executeDelegationRequest**(*address* delegator, *address* candidate) - executes any due delegation requests provided the address of a delegator and a candidate. Uses the [`executeDelegationRequest`](/builders/substrate/interfaces/features/staking/#:~:text=executeDelegationRequest(delegator, candidate)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `delegator` - address of the delegator - `candidate` - address of the candidate === "Returns" None. ??? function "**cancelDelegationRequest**(*address* candidate) - cancels any pending delegation requests provided the address of a candidate. Uses the [`cancelDelegationRequest`](/builders/substrate/interfaces/features/staking/#:~:text=cancelDelegationRequest(candidate)){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate to cancel the delegation request for === "Returns" None. ??? function "**setAutoCompound**(*address* candidate, *uint8* value, *uint256* candidateAutoCompoundingDelegationCount, *uint256* delegatorDelegationCount) - sets an auto-compound value for an existing delegation given an integer (no decimals) for the `value` between 0-100. Uses the [`setAutoCompound`](/builders/substrate/interfaces/features/staking/#:~:text=setAutoCompound){target=\_blank} method of the Parachain Staking Pallet" === "Parameters" - `candidate` - address of the candidate - `value` - uint8 percentage to auto-compound (0-100) - `candidateAutoCompoundingDelegationCount` - uint256 current number of auto-compounding delegations for the candidate - `delegatorDelegationCount` - uint256 current number of delegations from the delegator === "Returns" None. ## Interact with the Solidity Interface {: #interact-with-solidity-interface } ### Checking Prerequisites {: #checking-prerequisites } The below example is demonstrated on Moonbase Alpha, however, similar steps can be taken for Moonbeam and Moonriver. - Have MetaMask installed and [connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - Have an account with at least `{{networks.moonbase.staking.min_del_stake}}` token. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} !!! note The example below requires more than `{{networks.moonbase.staking.min_del_stake}}` token due to the minimum delegation amount plus gas fees. If you need more than the faucet dispenses, please contact us on Discord and we will be happy to help you. ### Remix Set Up {: #remix-set-up } 1. Click on the **File explorer** tab 2. Get a copy of [`StakingInterface.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/parachain-staking/StakingInterface.sol){target=\_blank} and paste the file contents into a Remix file named `StakingInterface.sol` ![Copying and Pasting the Staking Interface into Remix](/images/builders/ethereum/precompiles/features/staking/staking-1.webp) ### Compile the Contract {: #compile-the-contract } 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile StakingInterface.sol** ![Compiling StakingInterface.sol](/images/builders/ethereum/precompiles/features/staking/staking-2.webp) ### Access the Contract {: #access-the-contract } 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **ParachainStaking - StakingInterface.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the **At Address** field 4. Provide the address of the staking precompile for Moonbase Alpha: `{{networks.moonbase.precompiles.staking}}` and click **At Address** 5. The Parachain Staking precompile will appear in the list of **Deployed Contracts** ![Provide the address](/images/builders/ethereum/precompiles/features/staking/staking-3.webp) ### Delegate a Collator with Auto-Compounding {: #delegate-a-collator } For this example, you are going to be delegating a collator and setting up the percentage of rewards to auto-compound on Moonbase Alpha. Delegators are token holders who stake tokens, vouching for specific candidates. Any user that holds a minimum amount of {{networks.moonbase.staking.min_del_stake}} token in their free balance can become a delegator. When delegating a candidate, you can simultaneously set up auto-compounding. You'll be able to specify a percentage of your rewards that will automatically be applied to your total delegation. You don't have to set up auto-compounding right away, you can always do it at a later time. You can do your own research and select the candidate you desire. For this guide, the following candidate address will be used: `{{ networks.moonbase.staking.candidates.address1 }}`. In order to delegate a candidate, you'll need to determine the candidate's current delegation count, their auto-compounding delegation count, and your own delegation count. The candidate delegation count is the number of delegations backing a specific candidate. To obtain the candidate delegator count, you can call a function that the staking precompile provides. Expand the **PARACHAINSTAKING** contract found under the **Deployed Contracts** list, then: 1. Find and expand the **candidateDelegationCount** function 2. Enter the candidate address (`{{ networks.moonbase.staking.candidates.address1 }}`) 3. Click **call** 4. After the call is complete, the results will be displayed ![Call collator delegation count](/images/builders/ethereum/precompiles/features/staking/staking-4.webp) The auto-compounding delegation count is the amount of delegations that have auto-compounding configured. To determine the number of delegations that have auto-compounding set up, you can 1. Find and expand the **candidateAutoCompoundingDelegationCount** function 2. Enter the candidate address (`{{ networks.moonbase.staking.candidates.address1 }}`) 3. Click **call** 4. After the call is complete, the results will be displayed ![Get candidate auto-compounding delegation count](/images/builders/ethereum/precompiles/features/staking/staking-5.webp) The last item you'll need to retrieve is your delegation count. If you don't know your existing number of delegations, you can easily get them by following these steps: 1. Find and expand the **delegatorDelegationCount** function 2. Enter your address 3. Click **call** 4. After the call is complete, the results will be displayed ![Call delegator delegation count](/images/builders/ethereum/precompiles/features/staking/staking-6.webp) Now that you have obtained the [candidate delegator count](#:~:text=To obtain the candidate delegator count), the [auto-compounding delegation count](#:~:text=To determine the number of delegations that have auto-compounding set up), and your [number of existing delegations](#:~:text=If you don't know your existing number of delegations), you have all of the information you need to delegate a candidate and set up auto-compounding. To get started: 1. Find and expand the **delegateWithAutoCompound** function 2. Enter the candidate address you would like to delegate. For this example you can use `{{ networks.moonbase.staking.candidates.address1 }}` 3. Provide the amount to delegate in Wei. There is a minimum of `{{networks.moonbase.staking.min_del_stake}}` token to delegate, so the lowest amount in Wei is `{{networks.moonbase.staking.min_del_stake_wei}}` 4. Enter an integer (no decimals) between 0-100 to represent the percentage of rewards to auto-compound 5. Enter the delegation count for the candidate 6. Enter the auto-compounding delegation count for the candidate 7. Enter your delegation count 8. Press **transact** 9. MetaMask will pop up, you can review the details and confirm the transaction ![Delegate a Collator](/images/builders/ethereum/precompiles/features/staking/staking-7.webp) If you want to delegate without setting up auto-compounding, you can follow the previous steps, but instead of using **delegateWithAutoCompound**, you can use the **delegate** extrinsic. ### Verify Delegation {: #verify-delegation } To verify your delegation was successful, you can check the chain state in Polkadot.js Apps. First, add your MetaMask address to the [address book in Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network#/addresses){target=\_blank}. Navigate to **Accounts** and then **Address Book**, click on **Add contact**, and enter the following information: 1. Add your MetaMask address 2. Provide a nickname for the account 3. Click **Save** ![Add to Address Book](/images/builders/ethereum/precompiles/features/staking/staking-8.webp) To verify your delegation was successful, head to [Polkadot.js Apps](https://polkadot.js.org/apps/?rpc=wss://wss.api.moonbase.moonbeam.network#/chainstate){target=\_blank} and navigate to **Developer** and then **Chain State** 1. Select the **parachainStaking** pallet 2. Select the **delegatorState** query 3. Enter your address 4. Optionally, you can enable the **include option** slider if you want to provide a specific blockhash to query 5. Click the **+** button to return the results and verify your delegation !!! note You do not have to enter anything in the **blockhash to query at** field if you are looking for an overview of your delegations. ![Verify delegation](/images/builders/ethereum/precompiles/features/staking/staking-9.webp) ### Confirm Auto-Compounding Percentage {: #confirm-auto-compounding } You can confirm the percentage of rewards you've set to auto-compound in Remix using the `delegationAutoCompound` function of the Solidity interface: 1. Find and expand the **delegationAutoCompound** function 2. Enter your account you used to delegate with 3. Enter the candidate you've delegated 4. Click **call** 5. The response will appear below the **call** button ![Verify auto-compound percentage](/images/builders/ethereum/precompiles/features/staking/staking-10.webp) ### Set or Change the Auto-Compounding Percentage {: #set-or-change-auto-compounding } If you initially set up your delegation without auto-compounding or if you want to update the percentage on an existing delegation with auto-compounding set up, you can use the `setAutoCompound` function of the Solidity interface. You'll need to get the number of delegations with auto-compounding set up for the candidate you want to set or update auto-compounding for. You'll also need to retrieve your own delegation count. You can follow the instructions in the [Delegate a Collator with Auto-Compounding](#delegate-a-collator) section to get both of these items. Once you have the necessary information, you can take the following steps in Remix: 1. Find and expand the **setAutoCompound** function 2. Enter the candidate's account you want to set or update auto-compounding for 3. Enter a number 0-100 to represent the percentage of rewards you want to auto-compound 4. Enter the auto-compounding delegation count for the candidate 5. Enter your delegation count 6. Press **transact** 7. MetaMask will pop up, you can review the details and confirm the transaction ![Set or update auto-compound percentage](/images/builders/ethereum/precompiles/features/staking/staking-11.webp) ### Revoke a Delegation {: #revoke-a-delegation } As of [runtime version 1001](https://moonbeam.network/news/moonriver-technical-update-staking-changes-as-part-of-runtime-upgrade-1001){target=\_blank}, there have been significant changes to the way users can interact with various staking features. Including the way staking exits are handled. Exits now require you to schedule a request to exit or revoke a delegation, wait a delay period, and then execute the request. To revoke a delegation for a specific candidate and receive your tokens back, you can use the `scheduleRevokeDelegation` extrinsic. Scheduling a request does not automatically revoke your delegation, you must wait an [exit delay](#exit-delays), and then execute the request by using the `executeDelegationRequest` method. To revoke a delegation and receive your tokens back, head back over to Remix, then: 1. Find and expand the **scheduleRevokeDelegation** function 2. Enter the candidate address you would like to revoke the delegation for 3. Click **transact** 4. MetaMask will pop up, you can review the transaction details, and click **Confirm** ![Revoke delegation](/images/builders/ethereum/precompiles/features/staking/staking-12.webp) Once the transaction is confirmed, you must wait the duration of the exit delay before you can execute and revoke the delegation request. If you try to revoke it before the exit delay is up, your extrinsic will fail. After the exit delay has passed, you can go back to Remix and follow these steps to execute the due request: 1. Find and expand the **executeDelegationRequest** function 2. Enter the address of the delegator you would like to revoke the delegation for 3. Enter the candidate address you would like to revoke the delegation from 4. Click **transact** 5. MetaMask will pop up, you can review the transaction details, and click **Confirm** After the call is complete, the results will be displayed and the delegation will be revoked for the given delegator and from the specified candidate. You can also check your delegator state again on Polkadot.js Apps to confirm. If for any reason you need to cancel a pending scheduled request to revoke a delegation, you can do so by following these steps in Remix: 1. Find and expand the **cancelDelegationRequest** function 2. Enter the candidate address you would like to cancel the pending request for 3. Click **transact** 4. MetaMask will pop up, you can review the transaction details, and click **Confirm** You can check your delegator state again on Polkadot.js Apps to confirm that your delegation is still intact. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/interoperability/gmp/ --- BEGIN CONTENT --- --- title: GMP Precompile description: Learn about the GMP precompile on Moonbeam and how to use it with the Moonbeam Routed Liquidity program provided by bridges like Wormhole. keywords: solidity, ethereum, GMP, wormhole, moonbeam, bridge, connected, contracts, MRL categories: Precompiles, Ethereum Toolkit --- # Interacting with the GMP Precompile ## Introduction {: #introduction } Moonbeam Routed Liquidity (MRL) refers to Moonbeam’s use case as the port parachain for liquidity from origin chains into other Polkadot parachains. This is possible because of general message passing (GMP), where messages with arbitrary data and tokens can be sent across non-parachain blockchains through [chain-agnostic GMP protocols](/builders/interoperability/protocols/){target=\_blank}. These GMP protocols can combine with [Polkadot's XCM messaging system](/builders/interoperability/xcm/overview/){target=\_blank} to allow for seamless liquidity routing. The GMP precompile acts as an interface for Moonbeam Routed Liquidity, acting as a middleman between token-bearing messages from GMP protocols and parachains connected to Moonbeam via [XCMP](/builders/interoperability/xcm/overview/#xcm-transport-protocols){target=\_blank}. Currently, the GMP Precompile only supports the relaying of liquidity through the [Wormhole GMP protocol](/builders/interoperability/protocols/wormhole/){target=\_blank}. The GMP Precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.gmp}} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.gmp}} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.gmp}} ``` In practice, it is unlikely that a developer will have to directly interact with the precompile. GMP protocols' relayers interact with the precompile to complete cross-chain actions, so the origin chain that the cross-chain action originates is where the developer has the responsibility to ensure that the GMP precompile is used *eventually*. ## The GMP Solidity Interface {: #the-gmp-solidity-interface } [`Gmp.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/gmp/Gmp.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile. ??? code "Gmp.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The Gmp contract's address. address constant GMP_ADDRESS = 0x0000000000000000000000000000000000000816; /// @dev The Gmp contract's instance. Gmp constant GMP_CONTRACT = Gmp(GMP_ADDRESS); /// @author The Moonbeam Team /// @title Gmp precompile /// @dev Provides an endpoint to Gmp protocols which can automatically forward to XCM /// @custom:address 0x0000000000000000000000000000000000000816 interface Gmp { // TODO: Here we would specify the endpoints for each GMP protocol on a case by case basis. // These endpoints are basically the hand offs for each protocol -- where they delegate to // the target contract. // // This design should allow users to interact with this precompile with no changes to the // underlying GMP protocols by simply specifying the correct precompile as the target. /// Receive a wormhole VAA and process it /// /// @custom:selector f53774ab function wormholeTransferERC20(bytes memory vaa) external; } ``` The GMP precompile has one method: - **wormholeTransferERC20**(*bytes memory* vaa) - receives a Wormhole bridge transfer [verified action approval (VAA)](https://wormhole.com/docs/protocol/infrastructure/vaas/){target=\_blank}, mints tokens via the Wormhole token bridge, and forwards the liquidity to the custom payload’s [multilocation](/builders/interoperability/xcm/core-concepts/multilocations/){target=\_blank}. The payload is expected to be a precompile-specific SCALE encoded object, as explained in this guide's [Building the Payload for Wormhole](#building-the-payload-for-wormhole) section VAAs are payload-containing packages generated after origin-chain transactions and are discovered by Wormhole [Guardians](https://wormhole.com/docs/protocol/infrastructure/guardians/){target=\_blank}. The most common instance in which a user will have to interact with the precompile is during a recovery, where a relayer doesn’t complete an MRL transaction. For example, a user must search for the VAA that comes with their origin chain transaction and manually invoke the `wormholeTransferERC20` function. ## Building the Payload for Wormhole {: #building-the-payload-for-wormhole } Currently, the GMP precompile only supports sending liquidity with Wormhole, through Moonbeam, and into other parachains. The GMP precompile does not assist with a route from parachains back to Moonbeam and subsequently, Wormhole-connected chains. To send liquidity from a Wormhole-connected origin chain like Ethereum, users must invoke the [`transferTokensWithPayload` method](https://wormhole.com/docs/protocol/infrastructure/vaas/#token--message){target=\_blank} on the [origin-chain's deployment](https://wormhole.com/docs/protocol/infrastructure/core-contracts/#token-bridge){target=\_blank} of the [WormholeTokenBridge smart contract](https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/bridge/interfaces/ITokenBridge.sol){target=\_blank}. This function requires a bytes payload, which must be formatted as a SCALE encoded multilocation object wrapped within [another precompile-specific versioned type](https://github.com/moonbeam-foundation/moonbeam/blob/{{ networks.moonbase.spec_version }}/precompiles/gmp/src/types.rs#L25-L48){target=\_blank}. You may be unfamiliar with SCALE encoding and multilocations if you are unfamiliar with the Polkadot ecosystem. [SCALE encoding](https://docs.polkadot.com/polkadot-protocol/parachain-basics/data-encoding/){target=\_blank} is a compact form of encoding that Polkadot uses. The [`MultiLocation` type](https://wiki.polkadot.com/learn/learn-xcvm/){target=\_blank} is used to define a relative point in Polkadot, such as a specific account on a specific parachain (Polkadot blockchain). Moonbeam’s GMP protocol requires a multilocation to represent the destination for liquidity routing, which most likely means an account on another parachain. Whatever it is, this destination must be expressed as relative to Moonbeam. !!! remember Multilocations being relative is important, because a parachain team may erroneously give you a multilocation relative to their own chain, which can be different. Providing an incorrect multilocation can result in **loss of funds**! Each parachain will have its specific methods of interpreting a multilocation, and should confirm with the project that the multilocation that you formed is correct. However, you will most likely be forming a multilocation with an account. Multiple types of accounts can be included in a multilocation, which you must know beforehand when constructing your multilocation. The two most common are: - **AccountKey20** — an account ID that is 20-bytes in length, including Ethereum-compatible account IDs such as those on Moonbeam - **AccountId32** — an account ID that is 32-bytes in length, standard in Polkadot and its parachains The following multilocation templates target accounts on other parachains with Moonbeam as the relative origin. To use them, replace `INSERT_PARACHAIN_ID` with the parachain ID of the network you wish to send funds to and replace `INSERT_ADDRESS` with the address of the account you want to send funds to on that parachain. === "AccountId32" ```js { V4: { parents: 1, interior: { X2: [ { Parachain: 'INSERT_PARACHAIN_ID' }, { AccountId32: { id: 'INSERT_ADDRESS', }, }, ], }, }, }; ``` === "AccountKey20" ```js { V4: { parents: 1, interior: { X2: [ { Parachain: 'INSERT_PARACHAIN_ID' }, { AccountKey20: { key: 'INSERT_ADDRESS', }, }, ], }, } }; ``` It can be challenging to correctly SCALE encode the entire payload without the right tools, mainly due to the [custom types expected by the precompile](https://github.com/moonbeam-foundation/moonbeam/blob/{{ networks.moonbase.spec_version }}/precompiles/gmp/src/types.rs#L25-L48){target=\_blank}. Fortunately, the Polkadot.js API can assist with this. The versioned user action expected by the precompile accepts two versions: V1 and V2. V1 accepts the `XcmRoutingUserAction` type, which attempts to route the transferred assets to the destination defined by the multilocation. V2 accepts the `XcmRoutingUserActionWithFee` type, which also attempts to route the transferred assets to the destination and allows a fee to be paid. Relayers can use V2 to specify a fee on Moonbeam to relay the transaction to the given destination. The following script shows how to create a `Uint8Array` that can be used as a payload for the GMP precompile: === "V1" ```typescript import { ApiPromise, WsProvider } from '@polkadot/api'; enum MRLTypes { // Runtime defined MultiLocation. Allows for XCM versions 2, 3, and 4 XcmVersionedLocation = 'XcmVersionedLocation', // MRL payload (V1) that only defines the destination MultiLocation XcmRoutingUserAction = 'XcmRoutingUserAction', // Wrapper object for the MRL payload VersionedUserAction = 'VersionedUserAction', } // Parachain IDs of each parachain enum Parachain { MoonbaseBeta = 888, // Insert additional parachain IDs } // List of parachains that use ethereum (20) accounts const ETHEREUM_ACCOUNT_PARACHAINS = [Parachain.MoonbaseBeta]; // A function that creates a SCALE encoded payload to use with transferTokensWithPayload async function createMRLPayload( parachainId: Parachain, account: string ): Promise { // Create a multilocation object based on the target parachain's account type const isEthereumStyle = ETHEREUM_ACCOUNT_PARACHAINS.includes(parachainId); const multilocation = { V4: { parents: 1, interior: { X2: [ { Parachain: parachainId }, isEthereumStyle ? { AccountKey20: { key: account } } : { AccountId32: { id: account } }, ], }, }, }; // Creates an API for Moonbeam that defines MRL's special types const wsProvider = new WsProvider('wss://wss.api.moonbase.moonbeam.network'); const api = await ApiPromise.create({ provider: wsProvider, types: { [MRLTypes.XcmRoutingUserAction]: { destination: MRLTypes.XcmVersionedLocation, }, [MRLTypes.VersionedUserAction]: { _enum: { V1: MRLTypes.XcmRoutingUserAction }, }, }, }); // Format multilocation object as a Polkadot.js type const versionedLocation = api.createType( MRLTypes.XcmVersionedLocation, multilocation ); const userAction = api.createType(MRLTypes.XcmRoutingUserAction, { destination: versionedLocation, }); // Wrap and format the MultiLocation object into the precompile's input type const versionedUserAction = api.createType(MRLTypes.VersionedUserAction, { V1: userAction, }); // Disconnect the API api.disconnect(); // SCALE encode resultant precompile formatted objects return versionedUserAction.toU8a(); } ``` === "V2" ```typescript import { ApiPromise, WsProvider } from '@polkadot/api'; import { u256 } from '@polkadot/types'; enum MRLTypes { // Runtime defined MultiLocation. Allows for XCM versions 2 and 3 XcmVersionedLocation = 'XcmVersionedLocation', // MRL payload (V2) that defines the destination MultiLocation and a // fee for the relayer XcmRoutingUserActionWithFee = 'XcmRoutingUserActionWithFee', // Wrapper object for the MRL payload VersionedUserAction = 'VersionedUserAction', } // Parachain IDs of each parachain enum Parachain { MoonbaseBeta = 888, // Insert additional parachain IDs } // List of parachains that use ethereum (20) accounts const ETHEREUM_ACCOUNT_PARACHAINS = [Parachain.MoonbaseBeta]; // A function that creates a SCALE encoded payload to use with // transferTokensWithPayload async function createMRLPayload( parachainId: Parachain, account: string, fee: u256 ): Promise { // Create a multilocation object based on the target parachain's account // type const isEthereumStyle = ETHEREUM_ACCOUNT_PARACHAINS.includes(parachainId); const multilocation = { V4: { parents: 1, interior: { X2: [ { Parachain: parachainId }, isEthereumStyle ? { AccountKey20: { key: account } } : { AccountId32: { id: account } }, ], }, }, }; // Creates an API for Moonbeam that defines MRL's special types const wsProvider = new WsProvider('wss://wss.api.moonbase.moonbeam.network'); const api = await ApiPromise.create({ provider: wsProvider, types: { [MRLTypes.XcmRoutingUserActionWithFee]: { destination: MRLTypes.XcmVersionedLocation, fee: 'U256', }, [MRLTypes.VersionedUserAction]: { _enum: { V2: MRLTypes.XcmRoutingUserActionWithFee }, }, }, }); // Format multilocation object as a Polkadot.js type const versionedLocation = api.createType( MRLTypes.XcmVersionedLocation, multilocation ); const userAction = api.createType(MRLTypes.XcmRoutingUserActionWithFee, { destination: versionedLocation, fee, }); // Wrap and format the MultiLocation object into the precompile's input type const versionedUserAction = api.createType(MRLTypes.VersionedUserAction, { V2: userAction, }); // Disconnect the API api.disconnect(); // SCALE encode resultant precompile formatted objects return versionedUserAction.toU8a(); } ``` ## Restrictions {: #restrictions } The GMP precompile is currently in its early stages. There are many restrictions, and it only supports a “happy path” into parachains. Here are some restrictions that you should be aware of: - There is currently no fee mechanism. Relayers that run the forwarding of liquidity on Moonbeam to a parachain will be subsidizing transactions. This may change in the future - The precompile does not check to ensure that the destination chain supports the token that is being sent to it. **Incorrect multilocations may result in loss of funds** - Errors in constructing a multilocation will result in reverts, which will trap tokens and result in a loss of funds - There is currently no recommended path backward, from parachains to other chains like Ethereum. There is additional protocol-level work that must be done before a one-click method can be realized - Due to a restriction with the ERC-20 XC-assets, the only way to send tokens from a parachain back through Moonbeam is to have xcGLMR on the origin parachain and use it as a fee asset when sending tokens back --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/utility/eth-mainnet/ --- BEGIN CONTENT --- --- title: Ethereum MainNet Precompiles description: Learn how to use the standard precompiled contracts available on Ethereum such as ECRECOVER, SHA256, and more on Moonbeam. keywords: ethereum, moonbeam, ecrecover, sha256, ripemd-160, Bn128Add, Bn128Mul, Bn128Pairing categories: Precompiles, Ethereum Toolkit --- # Ethereum MainNet Precompiled Contracts ## Introduction {: #introduction } Precompiled contracts in Ethereum are contracts that include complex cryptographic computations, but do not require the overhead of the EVM. These precompiles can be used within the EVM to handle specific common operations such as hashing and signature schemes. The following precompiles are currently included: ecrecover, sha256, ripemd-160, Bn128Add, Bn128Mul, Bn128Pairing, the identity function, and modular exponentiation. These precompiles are natively available on Ethereum and, to maintain Ethereum compatibility, they are also available on Moonbeam. In this guide, you will learn how to use and/or verify these precompiles. ## Checking Prerequisites {: #checking-prerequisites } You need to install Node.js (for this example, you can use v16.x) and the npm package manager. You can download directly from [Node.js](https://nodejs.org/en/download/package-manager){target=\_blank} or in your terminal: === "Ubuntu" ```bash curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - sudo apt install -y nodejs ``` === "MacOS" ```bash # You can use homebrew (https://docs.brew.sh/Installation) brew install node # Or you can use nvm (https://github.com/nvm-sh/nvm) nvm install node ``` You can verify that everything is installed correctly by querying the version for each package: ```bash node -v ``` ```bash npm -v ``` As of writing this guide, the versions used were 15.2.1 and 7.0.8, respectively. You will also need to install the [Web3](https://web3js.readthedocs.io/en/latest){target=\_blank} package by executing: ```bash npm install --save web3 ``` To verify the installed version of Web3, you can use the `ls` command: ```bash npm ls web3 ``` As of writing this guide, the version used was 1.3.0. You will be also using [Remix](/builders/ethereum/dev-env/remix/){target=\_blank}, connecting it to the Moonbase Alpha TestNet via [MetaMask](/tokens/connect/metamask/){target=\_blank}. To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported [Endpoint Providers](/builders/get-started/endpoints/){target=\_blank}. ## Verify Signatures with ECRECOVER {: #verify-signatures-with-ecrecover } The main function of this precompile is to verify the signature of a message. In general terms, you feed `ecrecover` the transaction's signature values and it returns an address. The signature is verified if the address returned is the same as the public address that sent the transaction. The following will be a small example to showcase how to leverage this precompiled function. You'll need to retrieve the transaction's signature values (`v`, `r`, `s`). Therefore, you'll sign and retrieve the signed message where these values are: ```js const { Web3 } = require('web3'); // Provider const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network'); // Address and Private Key const address = '0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b'; const pk1 = '99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342'; const msg = web3.utils.sha3('supercalifragilisticexpialidocious'); async function signMessage(pk) { try { // Sign and get Signed Message const smsg = await web3.eth.accounts.sign(msg, pk); console.log(smsg); } catch (error) { console.error(error); } } signMessage(pk1); ``` This code will return the following object in the terminal: ```text { message: '0xc2ae6711c7a897c75140343cde1cbdba96ebbd756f5914fde5c12fadf002ec97', messageHash: '0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d50', v: '0x1b', r: '0x44287513919034a471a7dc2b2ed121f95984ae23b20f9637ba8dff471b6719ef', s: '0x7d7dc30309a3baffbfd9342b97d0e804092c0aeb5821319aa732bc09146eafb4', signature: '0x44287513919034a471a7dc2b2ed121f95984ae23b20f9637ba8dff471b6719ef7d7dc30309a3baffbfd9342b97d0e804092c0aeb5821319aa732bc09146eafb41b' } ``` With the necessary values, you can go to [Remix](/builders/ethereum/dev-env/remix/){target=\_blank} to test the precompiled contract. Note that this can also be verified with the Web3.js library, but in this case, you can go to Remix to be sure that it is using the precompiled contract on the blockchain. The Solidity code you can use to verify the signature is the following: ```solidity pragma solidity ^0.7.0; contract ECRECOVER { address addressTest = 0x12Cb274aAD8251C875c0bf6872b67d9983E53fDd; bytes32 msgHash = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d50; uint8 v = 0x1b; bytes32 r = 0x44287513919034a471a7dc2b2ed121f95984ae23b20f9637ba8dff471b6719ef; bytes32 s = 0x7d7dc30309a3baffbfd9342b97d0e804092c0aeb5821319aa732bc09146eafb4; function verify() public view returns (bool) { // Use ECRECOVER to verify address return (ecrecover(msgHash, v, r, s) == (addressTest)); } } ``` Using the [Remix compiler and deployment](/builders/ethereum/dev-env/remix/){target=\_blank} and with [MetaMask pointing to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank}, you can deploy the contract and call the `verify()` method that returns **true** if the address returned by `ecrecover` is equal to the address used to sign the message (related to the private key and needs to be manually set in the contract). ## Hashing with SHA256 {: #hashing-with-sha256 } This hashing function returns the SHA256 hash from the given data. To test this precompile, you can use this [SHA256 Hash Calculator tool](https://md5calc.com/hash/sha256){target=\_blank} to calculate the SHA256 hash of any string you want. In this case, you'll do so with `Hello World!`. You can head directly to Remix and deploy the following code, where the calculated hash is set for the `expectedHash` variable: ```solidity pragma solidity ^0.7.0; contract Hash256 { bytes32 public expectedHash = 0x7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069; function calculateHash() internal pure returns (bytes32) { string memory word = "Hello World!"; bytes32 hash = sha256(bytes(word)); return hash; } function checkHash() public view returns (bool) { return (calculateHash() == expectedHash); } } ``` Once the contract is deployed, you can call the `checkHash()` method that returns **true** if the hash returned by `calculateHash()` is equal to the hash provided. ## Hashing with RIPEMD160 {: #hashing-with-ripemd-160 } This hashing function returns a RIPEMD160 hash from the given data. To test this precompile, you can use this [RIPEMD160 Hash Calculator tool](https://md5calc.com/hash/ripemd160){target=\_blank} to calculate the RIPEMD160 hash of any string. In this case, you'll do so again with `Hello World!`. You'll reuse the same code as before, but use the `ripemd160` function. Note that it returns a `bytes20` type variable: ```solidity pragma solidity ^0.7.0; contract HashRipmd160 { bytes20 public expectedHash = hex"8476ee4631b9b30ac2754b0ee0c47e161d3f724c"; function calculateHash() internal pure returns (bytes20) { string memory word = "Hello World!"; bytes20 hash = ripemd160(bytes(word)); return hash; } function checkHash() public view returns (bool) { return (calculateHash() == expectedHash); } } ``` With the contract deployed, you can call the `checkHash()` method that returns **true** if the hash returned by `calculateHash()` is equal to the hash provided. ## BN128Add {: #bn128add } The BN128Add precompile implements a native elliptic curve point addition. It returns an elliptic curve point representing `(ax, ay) + (bx, by)` such that `(ax, ay)` and `(bx, by)` are valid points on the curve BN256. Currently there is no BN128Add support in Solidity, so it needs to be called with inline assembly. The following sample code can be used to call this precompile. ```solidity pragma solidity >=0.4.21; contract Precompiles { function callBn256Add( bytes32 ax, bytes32 ay, bytes32 bx, bytes32 by ) public returns (bytes32[2] memory result) { bytes32[4] memory input; input[0] = ax; input[1] = ay; input[2] = bx; input[3] = by; assembly { let success := call(gas, 0x06, 0, input, 0x80, result, 0x40) switch success case 0 { revert(0, 0) } } } } ``` Using the [Remix compiler and deployment](/builders/ethereum/dev-env/remix/){target=\_blank} and with [MetaMask pointing to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank}, you can deploy the contract and call the `callBn256Add(bytes32 ax, bytes32 ay, bytes32 bx, bytes32 by)` method to return the result of the operation. ## BN128Mul {: #bn128mul } The BN128Mul precompile implements a native elliptic curve multiplication with a scalar value. It returns an elliptic curve point representing `scalar * (x, y)` such that `(x, y)` is a valid curve point on the curve BN256. Currently there is no BN128Mul support in Solidity, so it needs to be called with inline assembly. The following sample code can be used to call this precompile. ```solidity pragma solidity >=0.4.21; contract Precompiles { function callBn256ScalarMul( bytes32 x, bytes32 y, bytes32 scalar ) public returns (bytes32[2] memory result) { bytes32[3] memory input; input[0] = x; input[1] = y; input[2] = scalar; assembly { let success := call(gas, 0x07, 0, input, 0x60, result, 0x40) switch success case 0 { revert(0, 0) } } } } ``` Using the [Remix compiler and deployment](/builders/ethereum/dev-env/remix/){target=\_blank} and with [MetaMask pointing to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank}, you can deploy the contract and call the `callBn256ScalarMul(bytes32 x, bytes32 y, bytes32 scalar)` method to return the result of the operation. ## BN128Pairing {: #bn128pairing } The BN128Pairing precompile implements elliptic curve pairing operation to perform zkSNARK verification. For more information, check out the [EIP-197 standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md){target=\_blank}. Currently there is no BN128Pairing support in Solidity, so it needs to be called with inline assembly. The following sample code can be used to call this precompile. ```solidity pragma solidity >=0.4.21; contract Precompiles { function callBn256Pairing( bytes memory input ) public returns (bytes32 result) { // input is a serialized bytes stream of (a1, b1, a2, b2, ..., ak, bk) from (G_1 x G_2)^k uint256 len = input.length; require(len % 192 == 0); assembly { let memPtr := mload(0x40) let success := call( gas(), 0x08, 0, add(input, 0x20), len, memPtr, 0x20 ) switch success case 0 { revert(0, 0) } default { result := mload(memPtr) } } } } ``` Using the [Remix compiler and deployment](/builders/ethereum/dev-env/remix/){target=\_blank} and with [MetaMask pointing to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank}, you can deploy the contract and call the `function callBn256Pairing(bytes memory input)` method to return the result of the operation. ## The Identity Function {: #the-identity-function } Also known as datacopy, this function serves as a cheaper way to copy data in memory. Currently there is no Identity Function support in Solidity, so it needs to be called with inline assembly. The following sample code (adapted to Solidity), can be used to call this precompiled contract: ```solidity pragma solidity ^0.7.0; contract Identity { bytes public memoryStored; function callDatacopy(bytes memory data) public returns (bytes memory) { bytes memory result = new bytes(data.length); assembly { let len := mload(data) if iszero( call( gas(), 0x04, 0, add(data, 0x20), len, add(result, 0x20), len ) ) { invalid() } } memoryStored = result; return result; } } ``` You can use this [Web3 Type Converter tool](https://web3-type-converter.onbrn.com){target=\_blank} to get bytes from any string, as this is the input of the `callDataCopy()` method. With the contract deployed, you can call the `callDataCopy()` method and verify if `memoryStored` matches the bytes that you pass in as an input of the function. ## Modular Exponentiation {: #modular-exponentiation } This precompile calculates the remainder when an integer `b` (base) is raised to the `e`-th power (the exponent), and is divided by a positive integer `m` (the modulus). The Solidity compiler does not support it, so it needs to be called with inline assembly. The following code was simplified to show the functionality of this precompile: ```solidity pragma solidity ^0.7.0; contract ModularCheck { uint public checkResult; // Function to Verify ModExp Result function verify(uint _base, uint _exp, uint _modulus) public { checkResult = modExp(_base, _exp, _modulus); } function modExp( uint256 _b, uint256 _e, uint256 _m ) public returns (uint256 result) { assembly { // Free memory pointer let pointer := mload(0x40) // Define length of base, exponent and modulus. 0x20 == 32 bytes mstore(pointer, 0x20) mstore(add(pointer, 0x20), 0x20) mstore(add(pointer, 0x40), 0x20) // Define variables base, exponent and modulus mstore(add(pointer, 0x60), _b) mstore(add(pointer, 0x80), _e) mstore(add(pointer, 0xa0), _m) // Store the result let value := mload(0xc0) // Call the precompiled contract 0x05 = bigModExp if iszero(call(not(0), 0x05, 0, pointer, 0xc0, value, 0x20)) { revert(0, 0) } result := mload(value) } } } ``` You can try this in [Remix](/builders/ethereum/dev-env/remix/){target=\_blank}. Use the function `verify()`, passing the base, exponent, and modulus. The function will store the value in the `checkResult` variable. ## P256 Verify {: #p256-verify } The P256Verify Precompile adds support for [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md){target=\_blank}, signature verification for Secp256r1 elliptic curve. This precompile adds a WASM implementation of the signature verification and is intended to be replaced by a native runtime function call once available. ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; contract P256Verify { function verify( bytes32 msg_hash, bytes32[2] memory signature, bytes32[2] memory public_key ) public view returns (bool) { bool output; bytes memory args = abi.encodePacked( msg_hash, signature[0], signature[1], public_key[0], public_key[1] ); bool success; assembly { success := staticcall(not(0), 0x100, add(args, 32), mload(args), output, 0x20) } require(success, "p256verify precompile call failed"); return output; } } ``` The file below contains two different test cases: one with a valid signature test and a second with an invalid signature test. ??? code "p256verifywithtests.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; contract P256Verify { function verify( bytes32 msg_hash, bytes32[2] memory signature, bytes32[2] memory public_key ) public view returns (bool) { bool output; bytes memory args = abi.encodePacked( msg_hash, signature[0], signature[1], public_key[0], public_key[1] ); bool success; assembly { success := staticcall(not(0), 0x100, add(args, 32), mload(args), output, 0x20) } require(success, "p256verify precompile call failed"); return output; } function test() public { bytes32[2] memory msg_hashes; bytes32[2][2] memory signatures; bytes32[2][2] memory public_keys; bool[2] memory expected_result; // Case 1 (valid) msg_hashes[0] = hex"b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955"; signatures[0][0] = hex"289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556"; signatures[0][1] = hex"d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da"; public_keys[0][0] = hex"3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdc"; public_keys[0][1] = hex"ef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1"; expected_result[0] = true; // Case 2 (invalid) msg_hashes[1] = hex"d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b"; signatures[1][0] = hex"6162630000000000000000000000000000000000000000000000000000000000"; signatures[1][1] = hex"6162630000000000000000000000000000000000000000000000000000000000"; public_keys[1][0] = hex"6162630000000000000000000000000000000000000000000000000000000000"; public_keys[1][1] = hex"6162630000000000000000000000000000000000000000000000000000000000"; expected_result[0] = false; for (uint256 i = 0; i < expected_result.length; i++) { bool result = verify(msg_hashes[i], signatures[i], public_keys[i]); if (expected_result[i]) { require(result, "Expected success"); } else { require(!result, "Expected failure"); } } } } ``` Using the [Remix compiler and deployment](/builders/ethereum/dev-env/remix/){target=\_blank} and with [MetaMask pointing to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank}, you can deploy the contract and call the `verify` method with the following parameters: === "Valid Signature" | Parameter | Value | |--------------|------------------------------------------------------------------------------------------------------------------------------------------------| | `msg_hash` | `0xb5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955` | | `signature` | `["0x289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556", "0xd262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da"]` | | `public_key` | `["0x3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdc", "0xef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1"]` | | Expected Result | `true` | === "Invalid Signature" | Parameter | Value | |-----------------|------------------------------------------------------------------------------------------------------------------------------------------------| | `msg_hash` | `0xd182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b` | | `signature` | `["0x6162630000000000000000000000000000000000000000000000000000000000", "0x6162630000000000000000000000000000000000000000000000000000000000"]` | | `public_key` | `["0x6162630000000000000000000000000000000000000000000000000000000000", "0x6162630000000000000000000000000000000000000000000000000000000000"]` | | Expected Result | `false` | You'll receive two booleans in response; the first one indicates whether the signature was valid, and the second indicates whether the call to the P256Verify precompile was successful. The second boolean should always return true; the first is the one to check to see if the signature is valid. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/utility/non-specific/ --- BEGIN CONTENT --- --- title: Non-Network Specific Precompiles description: Learn how to use precompiled contracts, which are not specific to Ethereum or Moonbeam, yet are supported for use in your application. keywords: ethereum, moonbeam, ECRecoverPublicKey, sha3FIPS256 categories: Precompiles, Ethereum Toolkit --- # Non-Network Specific Precompiled Smart Contracts ## Introduction {: #introduction } A precompiled contract, or precompile, is a set of programmed functionalities hard-coded into the blockchain client. Precompiles perform computationally heavy tasks, such as cryptographic processes like hashing. Moving these functionalities to the blockchain client serves the dual purpose of making the computation more efficient than using a traditional smart contract and ensuring everyone has access to the complete and accurate set of processes and algorithms required to operate correctly. Precompile functionality is bundled and shared under a smart contract address, which allows interactions similar to those of a traditional smart contract. Some precompiled contracts are not specific to Ethereum or Moonbeam, but are supported for use in your Moonbeam application. The nonspecific precompiles currently included in this category are the `ECRecoverPublicKey` and `SHA3FIPS256` precompiles. In the next section, you will learn more about the functionalities included in these precompiles. ## Retrieve a Public Key with ECRecoverPublicKey {: verifying-signatures-ecrecoverpublickey } The primary function of the `ECRecoverPublicKey` precompile is to recover the public key used to create a digital signature from a given message hash and signature. This precompile is similar to [ECRecover](/builders/ethereum/precompiles/utility/eth-mainnet/#verify-signatures-with-ecrecover/){target=\_blank}, with the exception of returning the public key of the account that signed the message rather than the account address. In the following sections, you will learn how to use the `ECRecoverPublicKey` precompile. ### Checking Prerequisites {: #checking-prerequisites } You need to install Node.js (for this example, you can use v16.x) and the npm package manager. You can download directly from [Node.js](https://nodejs.org/en/download/package-manager){target=\_blank} or in your terminal: === "Ubuntu" ```bash curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - sudo apt install -y nodejs ``` === "MacOS" ```bash # You can use homebrew (https://docs.brew.sh/Installation) brew install node # Or you can use nvm (https://github.com/nvm-sh/nvm) nvm install node ``` You can verify that everything is installed correctly by querying the version for each package: ```bash node -v ``` ```bash npm -v ``` The versions used in this example are v20.15.0 (Node.js) and 10.7.0 (npm). You will also need to install the [Web3](https://web3js.readthedocs.io/en/latest){target=\_blank} package by executing: ```bash npm install --save web3 ``` To verify the installed version of Web3, you can use the `ls` command: ```bash npm ls web3 ``` This example uses version 4.11.1. You will also use [Remix](/builders/ethereum/dev-env/remix/){target=\_blank}, connecting it to the Moonbase Alpha TestNet via [MetaMask](/tokens/connect/metamask/){target=\_blank}. To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported [Endpoint Providers](/builders/get-started/endpoints/){target=\_blank}. ### Retrieve Transaction Signature Values To use the `ECRecoverPublicKey` precompile, you must first sign a message to create and retrieve the message hash and transaction signature values (`v`, `r`, `s`) to pass as arguments in the contract call. Always use security best practices when handling private keys. Create a new file called `signMessage.js` in your project directory: ```bash touch signMessage.js ``` Open `signMessage.js` in your code editor and add the following script to initialize Web3 with Moonbase Alpha TestNet, sign and hash the message, and return the signature values: ```js title="signMessage.js" const { Web3 } = require('web3'); const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network'); // Address and private key const address = 'INSERT_RECEIVER_ADDRESS'; const pk1 = 'INSERT_SENDER_PRIVATE_KEY'; const msg = web3.utils.sha3('supercooltestmessage'); async function signMessage(pk) { try { // Sign and get signed message const smsg = await web3.eth.accounts.sign(msg, pk); console.log(smsg); } catch (error) { console.error(error); } } signMessage(pk1); ``` Return to your terminal command line to run the script with this command: ```bash node signMessage.js ``` This code will return the following object in the terminal:
node signMessage.js { message: '0x5836e21a51f25aad199e2e0feb5ca19673ed56b3811285f5124d7a8171d75851', messageHash: '0xa69b720d0293b9e8f4e471afb80f9d410b825abe5ce524e7d5755fd2a00bf9de', v: '0x1b', r: '0xb7d4783ee3b34d6fbc419d5b7bc67002c511322c5c71b49a7d78a8b7e9c5b30a', s: '0x4e5939eaef3917b1cb09af9e632cc9a727b64191b7ee40a6ae34f6fdde60a371', signature: '0xb7d4783ee3b34d6fbc419d5b7bc67002c511322c5c71b49a7d78a8b7e9c5b30a4e5939eaef3917b1cb09af9e632cc9a727b64191b7ee40a6ae34f6fdde60a3711b' }
Save these values as you will need them in the next section. ### Test ECRecoverPublicKey Contract You can now visit [Remix](https://remix.ethereum.org/){target=\_blank} to test the precompiled contract. Note that you could also use the Web3.js library, but in this case, you can go to Remix to ensure it is using the precompiled contract on the blockchain. The Solidity code you can use to retrieve the public key is the following: ```solidity title="RecoverPublicKey.sol" // SPDX-License-Identifier: MIT pragma solidity >=0.8.2 <0.9.0; contract RecoverPublicKey { function recoverPublicKey( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) public view returns (bytes memory) { address precompileAddress = 0x0000000000000000000000000000000000000402; (bool success, bytes memory publicKey) = precompileAddress.staticcall( abi.encodeWithSignature( "ECRecoverPublicKey(bytes32,uint8,bytes32,bytes32)", hash, v, r, s ) ); require(success, "ECRecoverPublicKey failed"); return publicKey; } } ``` Using the [Remix compiler and deployment](/builders/ethereum/dev-env/remix/){target=\_blank} and with [MetaMask pointing to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank}, you can deploy the contract and call the `recoverPublicKey()` method which returns the public key for the account that signed the message. You can now use this public key value for other cryptographic functions and verifications. ![Returned Public Key on Remix](/images/builders/ethereum/precompiles/utility/nonspecific/nonspecific-1.webp) ## Create a Hash with SHA3FIPS256 {: #create-a-hash-with-sha3fips256 } SHA3-256 is part of the SHA-3 family of cryptographic hashes codified in [FIPS202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf){target=\_blank} that produces an output 256 bits in length. Although the name is similar to SHA256, the SHA-3 family is built with an entirely different algorithm and accordingly produces a different hash output than SHA256 for the same input. You can verify this yourself using this [SHA3-256 Hash Calculator tool](https://md5calc.com/hash/sha3-256){target=\_blank}. After calculating the SHA3-256 output, change the algorithm in the drop-down selector to SHA256 and take note of the resulting output. Currently there is no SHA3-256 support in Solidity, so it needs to be called with inline assembly. The following sample code can be used to call this precompile. ```solidity pragma solidity ^0.7.0; contract Precompiles { function sha3fips(bytes memory data) public view returns (bytes32) { bytes32[1] memory h; assembly { if iszero( staticcall(not(0), 0x400, add(data, 32), mload(data), h, 32) ) { invalid() } } return h[0]; } } ``` Using [Remix](/builders/ethereum/dev-env/remix/){target=\_blank} with [MetaMask pointing to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank}, you can deploy the contract and call the `sha3fips(bytes memory data)` method to return the encoded string of the data parameter.
The information presented herein is for informational purposes only and has been provided by third parties. Moonbeam does not endorse any project listed and described on the Moonbeam docs website (https://docs.moonbeam.network/).
--- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/utility/registry/ --- BEGIN CONTENT --- --- title: Precompile Registry description: Learn how to access and interact with the Precompile Registry on Moonbeam, which can be used to check if a given address is a precompile and if it is supported. categories: Precompiles, Ethereum Toolkit --- # Precompile Registry on Moonbeam ## Introduction {: #introduction } The Precompile Registry serves as a single source of truth for the available [precompiles on Moonbeam](/builders/ethereum/precompiles/overview/){target=\_blank}. The Precompile Registry can be used to determine if an address corresponds to a precompile and whether or not a precompile is active or deprecated. This is particularly useful when there are upstream changes within the Substrate and Polkadot ecosystems that result in backward-incompatible changes to precompiles. Developers can design an exit strategy to ensure their dApp recovers gracefully in these scenarios. The Precompile Registry also serves an additional purpose, as it allows any user to set "dummy code" (`0x60006000fd`) for precompiles, which makes precompiles callable from Solidity. This is necessary as precompiles on Moonbeam, by default, don't have bytecode. The "dummy code" can bypass checks in Solidity that ensure contract bytecode exists and is non-empty. The Registry Precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.registry }} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.registry }} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.registry }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Precompile Registry Solidity Interface {: #the-solidity-interface } [`PrecompileRegistry.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/precompile-registry/PrecompileRegistry.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's methods. ??? code "PrecompileRegistry.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The Precompile Registry contract's address. address constant PRECOMPILE_REGISTRY_ADDRESS = 0x0000000000000000000000000000000000000815; /// @dev The Precompile Registry contract's instance. PrecompileRegistry constant PRECOMPILE_REGISTRY_CONTRACT = PrecompileRegistry(PRECOMPILE_REGISTRY_ADDRESS); /// @author The Moonbeam Team /// @title Precompile Registry /// @dev Interface to the set of available precompiles. interface PrecompileRegistry { /// @dev Query if the given address is a precompile. Note that deactivated precompiles /// are still considered precompiles and will return `true`. /// @param a: Address to query /// @return output Is this address a precompile? /// @custom:selector 446b450e function isPrecompile(address a) external view returns (bool); /// @dev Query if the given address is an active precompile. Will return false if the /// address is not a precompile or if this precompile is deactivated. /// @param a: Address to query /// @return output Is this address an active precompile? /// @custom:selector 6f5e23cf function isActivePrecompile(address a) external view returns (bool); /// @dev Update the account code of a precompile address. /// As precompiles are implemented inside the Runtime, they don't have a bytecode, and /// their account code is empty by default. However in Solidity calling a function of a /// contract often automatically adds a check that the contract bytecode is non-empty. /// For that reason a dummy code (0x60006000fd) can be inserted at the precompile address /// to pass that check. This function allows any user to insert that code to precompile address /// if they need it. /// @param a: Address of the precompile. /// @custom:selector 48ceb1b4 function updateAccountCode(address a) external; } ``` The interface includes the following functions: ??? function "**isPrecompile**(*address* a) - returns a boolean indicating whether a given address is a precompile or not. Returns `true` for active and deprecated precompiles" === "Parameters" - `a` - address to check if it is a precompile === "Returns" - `bool` whether the address is a precompile (active or deprecated) ??? function "**isActivePrecompile**(*address* a) - returns a boolean indicating whether a given address is an active precompile or not. Returns `false` if a precompile has been deprecated" === "Parameters" - `a` - address to check if it is an active precompile === "Returns" - `bool` whether the address is an active precompile ??? function "**updateAccountCode**(*address* a) - updates a given precompile's bytecode with dummy code (`0x60006000fd`) given the address of the precompile. Precompiles, by default, don't have bytecode associated with them. This function can be used to add dummy bytecode to bypass requirements in Solidity that check if a contract's bytecode is not empty before its functions can be called" === "Parameters" - `a` - address of the precompile to update with dummy bytecode === "Returns" None. ## Interact with the Precompile Registry Solidity Interface {: #interact-with-precompile-registry-interface } The following sections will cover how to interact with the Registry Precompile from [Remix](/builders/ethereum/dev-env/remix/){target=\_blank} and [Ethereum libraries](/builders/ethereum/libraries/){target=\_blank}, such as [Ethers.js](/builders/ethereum/libraries/ethersjs/){target=\_blank}, [Web3.js](/builders/ethereum/libraries/web3js/){target=\_blank}, and [Web3.py](/builders/ethereum/libraries/web3py/){target=\_blank}. The examples in this guide will be on Moonbase Alpha. To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported [Endpoint Providers](/builders/get-started/endpoints/){target=\_blank}. ### Use Remix to Interact with the Precompile Registry {: #use-remix } To quickly get started with [Remix](/builders/ethereum/dev-env/remix/){target=\_blank}, the [Precompile Registry contract has been loaded from GitHub](https://remix.ethereum.org/#url=https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/precompile-registry/PrecompileRegistry.sol){target=\_blank}. You can also create a new file in Remix and manually paste in the contents of the [`PrecompileRegistry.sol`](#the-solidity-interface) contract. ![Add the Precompile Registry Interface to Remix](/images/builders/ethereum/precompiles/utility/registry/registry-1.webp) Then you can take the following steps to compile, deploy, and interact with the Precompile Registry: 1. From the **Compile** tab, click on **Compile PrecompileRegistry.sol** to compile the contract. A green checkmark will appear upon successfully compiling the contract ![Compile the Precompile Registry contract](/images/builders/ethereum/precompiles/utility/registry/registry-2.webp) 2. From the **Deploy and run transactions** tab, you can load the Precompile Registry using its address: 1. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down and you've connected MetaMask to Moonbase Alpha 2. Ensure **PrecompileRegistry** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the Precompile in the **At Address** field 3. Provide the address of the Precompile Registry for Moonbase Alpha: `{{ networks.moonbase.precompiles.registry }}` and click **At Address** 4. The Precompile Registry will appear in the list of **Deployed Contracts** ![Access the Precompile Registry contract](/images/builders/ethereum/precompiles/utility/registry/registry-3.webp) 3. You can interact with any of the precompile's methods. Under **Deployed Contracts**, expand the Precompile Registry to view the list of methods. For example, you can use the **isPrecompile** function to check if an address is a precompile ![Interact with the Precompile Registry contract](/images/builders/ethereum/precompiles/utility/registry/registry-4.webp) ### Use Ethereum Libraries to Interact with the Precompile Registry {: #use-ethereum-libraries } To interact with the Precompile Registry's Solidity interface with an Ethereum library, you'll need the Precompile Registry's ABI. ??? code "Precompile Registry ABI" ```js [ { "inputs": [ { "internalType": "address", "name": "a", "type": "address" } ], "name": "isActivePrecompile", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "a", "type": "address" } ], "name": "isPrecompile", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "a", "type": "address" } ], "name": "updateAccountCode", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] ``` Once you have the ABI, you can interact with the Registry using the Ethereum library of your choice. Generally speaking, you'll take the following steps: 1. Create a provider 2. Create a contract instance of the Precompile Registry 3. Interact with the Precompile Registry's functions !!! remember The following snippets are for demo purposes only. Never store your private keys in a JavaScript or Python file. === "Ethers.js" ```js import { ethers } from 'ethers'; // Import Ethers library import ABI from './precompileRegistryABI.js'; // Import Precompile Registry ABI const privateKey = 'INSERT_PRIVATE_KEY'; // Create Ethers provider and signer const provider = new ethers.JsonRpcProvider( 'https://rpc.api.moonbase.moonbeam.network' ); const signer = new ethers.Wallet(privateKey, provider); // Create interface for the Precompile Registry const precompileRegistry = new ethers.Contract( '0x0000000000000000000000000000000000000815', ABI, signer ); // Interact with the Precompile Registry const isActivePrecompile = async () => { const proxyPrecompile = '0x000000000000000000000000000000000000080b'; // Check if the Proxy Precompile is a precompile const isPrecompile = await precompileRegistry.isPrecompile(proxyPrecompile); // Should return 'Address is a precompile: true' console.log(`Address is a precompile: ${isPrecompile}`); // Check if the Proxy Precompile is an active precompile const isActivePrecompile = await precompileRegistry.isActivePrecompile( proxyPrecompile ); // Should return 'Address is an active precompile: true' console.log(`Address is an active precompile: ${isActivePrecompile}`); }; isActivePrecompile(); ``` === "Web3.js" ```js import { Web3 } from 'web3'; import ABI from './precompileRegistryABI.js'; // Import Precompile Registry ABI const privateKey = 'INSERT_PRIVATE_KEY'; // Create provider const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network'); // Create interface for the Precompile Registry const precompileRegistry = new web3.eth.Contract( ABI, '0x0000000000000000000000000000000000000815', { from: web3.eth.accounts.privateKeyToAccount(privateKey).address } ); // Interact with the Precompile Registry const isActivePrecompile = async () => { const proxyPrecompile = '0x000000000000000000000000000000000000080b'; // Check if the Proxy Precompile is a precompile const isPrecompile = await precompileRegistry.methods.isPrecompile( proxyPrecompile ).call(); // Should return 'Address is a precompile: true' console.log(`Address is a precompile: ${isPrecompile}`); // Check if the Proxy Precompile is an active precompile const isActivePrecompile = await precompileRegistry.methods.isActivePrecompile(proxyPrecompile).call(); // Should return 'Address is an active precompile: true' console.log(`Address is a precompile: ${isActivePrecompile}`); }; isActivePrecompile(); ``` === "Web3.py" ```py from web3 import Web3 abi = "INSERT_PRECOMPILE_REGISTRY_ABI" # Paste or import the Precompile Registry ABI private_key = "INSERT_PRIVATE_KEY" # Create provider web3 = Web3(Web3.HTTPProvider("https://rpc.api.moonbase.moonbeam.network")) # Create interface for the Precompile Registry precompile_registry = web3.eth.contract( address="0x0000000000000000000000000000000000000815", abi=abi ) # Interact with the Precompile Registry def is_active_precompile(): proxy_precompile = "0x000000000000000000000000000000000000080b" # Check if the Proxy Precompile is a precompile is_precompile = precompile_registry.functions.isPrecompile(proxy_precompile).call() # Should return 'Address is a precompile: true' print("Address is a precompile: ", is_precompile) # Check if the Proxy Precompile is an active precompile is_active_precompile = precompile_registry.functions.isActivePrecompile( proxy_precompile ).call() # Should return 'Address is an active precompile: true' print("Address is an active precompile: ", is_active_precompile) is_active_precompile() ``` --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/utility/relay-data-verifier/ --- BEGIN CONTENT --- --- title: Relay Data Verifier Precompile Contract description: Learn how to verify data availability and authenticity on the relay chain via a Solidity interface with Moonbeam's Relay Data Verifier Precompile contract. keywords: solidity, ethereum, verify, proof, relay chain, transaction, moonbeam, precompiled, contracts categories: Precompiles, Ethereum Toolkit --- # Interacting with the Relay Data Verifier Precompile ## Introduction {: #introduction } Polkadot relies on state proofs to guarantee data integrity at a particular time. A state proof is a concise, cryptographic data structure representing a specific subset of transactions or state data within a trie. It consists of a set of hashes that form a path from the target data to the root hash stored in the block header. A client can independently reconstruct the root hash and compare it with the original stored in the block header by providing a state proof. If the reconstructed root hash matches the original, it confirms the target data's authenticity, validity, and inclusion within the blockchain. Polkadot's unique architecture and parachain block validation process means blockchains like Moonbeam have the relay chain storage root hash in their state. Consequently, Moonbeam can provide a mechanism to verify a relay chain state by checking the proof against the stored storage root hash. Moonbeam's [relay data verifier precompiled](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/relay-data-verifier/RelayDataVerifier.sol){target=\_blank} contract provides an easy way for smart contracts to programmatically build functions that rely on verifying relay chain state in contract calls. Consequently, no oracles are needed to feed relay chain data to Moonbeam. This functionality is readily available at the following contract addresses: === "Moonbeam" ```text {{networks.moonbeam.precompiles.relay_data_verifier }} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.relay_data_verifier }} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.relay_data_verifier }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Relay Data Verifier Solidity Interface {: #the-relay-data-verifier-solidity-interface } [`RelayDataVerifier.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/relay-data-verifier/RelayDataVerifier.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's methods. ??? code "RelayDataVerifier.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The RelayDataVerifier contract's address. address constant RELAY_DATA_VERIFIER_ADDRESS = 0x0000000000000000000000000000000000000819; /// @dev The RelayDataVerifier contract's instance. RelayDataVerifier constant RELAY_DATA_VERIFIER_CONTRACT = RelayDataVerifier( RELAY_DATA_VERIFIER_ADDRESS ); /// @author The Moonbeam Team /// @title Relay Proof Verifier Interface /// @dev The interface that Solidity contracts use to interact with the Relay Proof Verifier /// precompile. /// A typical workflow to verify relay chain data is the following: /// 1. Moonbeam RPC Call: Call `latestRelayBlockNumber` function to get the latest relay /// block number tracked by the chain in `pallet-storage-root`. /// 2. Relay RPC Call: Call `chain_getBlockHash(blockNumber)` RPC method to get the relay block hash /// for the block number obtained in step 1. /// 3. Relay RPC Call: Call `state_getReadProof(keys, at)` RPC method where `at` /// is the relay block hash obtained in step 2 to get the 'ReadProof` of the entries. /// 4. Moonbeam RPC Call: Submit an ethereum transaction (directly or through a SC) to call the /// `verifyEntry` or `verifyEntries` function to verify the data against the relay block /// number. The call data contain the relay block number obtained in step 1, and the read /// proof generated in step 3, along with the key/s to verify. /// @custom:address 0x0000000000000000000000000000000000000819 interface RelayDataVerifier { /// @dev ReadProof struct returned by the `state_getReadProof` RPC method. struct ReadProof { // The block hash against which the proof is generated bytes32 at; /// The storage proof bytes[] proof; } /// @dev Verifies a storage entry in the Relay Chain using a relay block number and a storage /// proof. This function takes a relay block number, a storage proof, and the key of the storage /// entry to verify. It returns the value associated with the key if the verification is /// successful. /// @custom:selector 27001faa /// @param relayBlockNumber The relay block number against which the entry is being verified. /// @param readProof The storage proof used to verify the entry. /// @param key The key of the storage entry to verify. /// @return value The value associated with the key, returned as a bytes array. function verifyEntry( uint32 relayBlockNumber, ReadProof calldata readProof, bytes calldata key ) external returns (bytes memory value); /// @dev Verifies a set of entries in the Relay Chain and returns the corresponding values. /// This function takes a relay block number, a storage proof, and an array of keys for the /// storage entries to verify. It returns an array of values associated with the keys, in the /// same order as the keys. /// @custom:selector 2da33a45 /// @param relayBlockNumber The relay block number for which the data is being verified. /// @param readProof The storage proof used to verify the data. /// @param keys The keys of the storage entries to verify. /// @return values The values associated with the keys, returned in the same order as the keys. function verifyEntries( uint32 relayBlockNumber, ReadProof calldata readProof, bytes[] calldata keys ) external returns (bytes[] memory values); /// @dev Returns the latest relay block number that has a storage root stored on-chain. /// @custom:selector aed36869 /// @return relayBlockNumber the lastest relay block number function latestRelayBlockNumber() external view returns (uint32 relayBlockNumber); } ``` The interface includes the following functions: ???+ function "**latestRelayBlockNumber**() — retrieves the most recent relay chain block that has its storage root stored on the blockchain itself" === "Parameters" None === "Returns" The latest relay block number that has a storage root stored on-chain. ??? function "**verifyEntry**(_uint32_ relayBlockNumber, _ReadProof_ calldata readProof, _bytes_ callData key) — verifies a storage entry in the relay chain using a relay block number, a storage proof, and the storage key. It returns the value associated with the key if the verification is successful" === "Parameters" - `relayBlockNumber` - the relay block number for which the data is being verified. The latest relay block number can be obtained from the `latestRelayBlockNumber()` function - `readProof` - a struct defined in the precompile contract, containing the storage proof used to verify the data. The `ReadProof` struct is defined as: ``` struct ReadProof { // The block hash against which the proof is generated bytes32 at; /// The storage proof bytes[] proof; } ``` - `key` - the storage key for the generated proof === "Returns" When performing a [static call](https://docs.ethers.org/v6/api/contract/#BaseContractMethod-staticCall){target=\_blank} on the `verifyEntry` function, you can view the returned value associated with the key in hexadecimal format. ```js '0x01000000040000000100000000000000f88ce384dca20000000000000000000000370589030a0000000000000000000000203d88792d0000000000000000000000000000000000000000000000000080' ``` ??? function "**verifyEntries**(_uint32_ relayBlockNumber, _ReadProof_ calldata readProof, _bytes[]_ callData keys) — verifies a set of entries in the relay chain and returns the corresponding values. This function takes a relay block number, a storage proof, and an array of storage keys to verify. It returns an array of values associated with the keys, in the same order as the keys" === "Parameters" - `relayBlockNumber` - the relay block number for which the data is being verified. The latest relay block number can be obtained from the `latestRelayBlockNumber()` function - `readProof` - a struct defined in the precompile contract, containing the storage proof used to verify the data. The `ReadProof` struct is defined as: ``` struct ReadProof { // The block hash against which the proof is generated bytes32 at; /// The storage proof bytes[] proof; } ``` - `keys` - the storage keys for the generated proof === "Returns" When performing a [static call](https://docs.ethers.org/v6/api/contract/#BaseContractMethod-staticCall){target=\_blank} on the `verifyEntries` function, you can view an array containing the corresponding values mapped to their respective keys, represented in hexadecimal format. ```js ['0x01000000040000000100000000000000f88ce384dca20000000000000000000000370589030a0000000000000000000000203d88792d0000000000000000000000000000000000000000000000000080'] ``` ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } A typical workflow to verify relay chain data involves the following steps: 1. **Moonbeam RPC call** - call the `latestRelayBlockNumber` function to get the latest relay block number tracked by the chain in the `pallet-storage-root` 2. **Relay RPC call** - call the `chain_getBlockHash(blockNumber)` RPC method to get the relay block hash for the block number obtained in step one 3. **Relay RPC call** - call the `state_getReadProof(keys, at)` RPC method to retrieve the storage proof, where `at` is the relay block hash obtained in step two, and `keys` is an array of strings which contains the keys for target storage items. For `@polkadot/api`, it can be obtained via `api.query.module.key()` function 4. **Moonbeam RPC call** - submit an Ethereum transaction to call the `verifyEntry` or `verifyEntries` function to verify the data against the relay block number. The call data should contain the relay block number obtained in step one, the read proof generated in step three, and the key(s) to verify The following sections will cover how to interact with the Relay Data Verifier Precompile using Ethereum libraries, such as Ethers.js, Web3.js, and Web3.py. The examples in this guide will be on Moonbase Alpha. ### Checking Prerequisites {: #checking-prerequisites } To follow along with this tutorial, you will need to have: - Create or have an account on Moonbase Alpha to test out the different features in the precompile - The account will need to be funded with `DEV` tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Using Ethereum Libraries {: #using-ethereum-libraries } To interact with the Solidity interface using an Ethereum library, you'll need the precompile's ABI (Application Binary Interface). The ABI for the Relay Chain Data Verifier Precompile is as follows: ??? code "Relay Data Verifier Precompile ABI" ```js [ { inputs: [], name: 'latestRelayBlockNumber', outputs: [ { internalType: 'uint32', name: 'relayBlockNumber', type: 'uint32', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'uint32', name: 'relayBlockNumber', type: 'uint32', }, { components: [ { internalType: 'bytes32', name: 'at', type: 'bytes32', }, { internalType: 'bytes[]', name: 'proof', type: 'bytes[]', }, ], internalType: 'struct RelayDataVerifier.ReadProof', name: 'readProof', type: 'tuple', }, { internalType: 'bytes[]', name: 'keys', type: 'bytes[]', }, ], name: 'verifyEntries', outputs: [ { internalType: 'bytes[]', name: 'values', type: 'bytes[]', }, ], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'uint32', name: 'relayBlockNumber', type: 'uint32', }, { components: [ { internalType: 'bytes32', name: 'at', type: 'bytes32', }, { internalType: 'bytes[]', name: 'proof', type: 'bytes[]', }, ], internalType: 'struct RelayDataVerifier.ReadProof', name: 'readProof', type: 'tuple', }, { internalType: 'bytes', name: 'key', type: 'bytes', }, ], name: 'verifyEntry', outputs: [ { internalType: 'bytes', name: 'value', type: 'bytes', }, ], stateMutability: 'nonpayable', type: 'function', }, ]; ``` Once you have the ABI, you can interact with the precompile using the Ethereum library of your choice, such as [Ethers.js](/builders/ethereum/libraries/ethersjs/){target=\_blank}, [Web3.js](/builders/ethereum/libraries/web3js/){target=\_blank}, or [Web3.py](/builders/ethereum/libraries/web3py/){target=\_blank}. The general steps are as follows: 1. Create a provider 2. Create a contract instance of the precompile 3. Interact with the precompile's functions The provided code example demonstrates how to use the Ethers.js library to interact with the Moonbase Alpha network and its relay chain, verifying a data entry using the `verifyEntry` function. !!! note The code snippets presented in the following sections are not meant for production environments. Please make sure you adapt it for each use case. === "Ethers.js" ```js // For reading local ABI file import * as fs from 'fs'; // Import Ethers library, to interact with Moonbeam networks import { ethers } from 'ethers'; // Import Polkadot library, to interact with relay chain import { ApiPromise, WsProvider } from '@polkadot/api'; const abi = JSON.parse(fs.readFileSync('./RelayChainDataVerifierABI.json')); const privateKey = 'INSERT_PRIVATE_KEY'; const precompileAddress = '0x0000000000000000000000000000000000000819'; const moonbeamURL = 'https://rpc.api.moonbase.moonbeam.network'; const relayURL = 'wss://relay.api.moonbase.moonbeam.network'; // Create Ethers provider and signer const provider = new ethers.JsonRpcProvider(moonbeamURL); const signer = new ethers.Wallet(privateKey, provider); const precompileContract = new ethers.Contract(precompileAddress, abi, signer); async function run() { // Create provider for relay chain const wsProvider = new WsProvider(relayURL); const api = await ApiPromise.create({ provider: wsProvider }); // Get the storage key for a random account on relay chain const key = api.query.system.account.key( '5CBATpb3yvEM4mhX9Dw3tyuqiWKhq9YBG6ugSbodRUSbodoU' ); // Find the latest available relay chain block number from Moonbeam const blockNum = await precompileContract.latestRelayBlockNumber(); // Get the block hash and storage proof from relay chain const blockHash = await api.rpc.chain.getBlockHash(blockNum); const proof = await api.rpc.state.getReadProof([key], blockHash); // This tx will be rejected if the verification failed const receipt = await precompileContract.verifyEntry(blockNum, proof, key); await receipt.wait(); console.log(receipt.hash); } await run(); ``` === "Web3.js" ```js // For reading local ABI file import * as fs from 'fs'; // Import web3js library, to interact with Moonbeam networks import { Web3 } from 'web3'; // Import Polkadot library, to interact with relay chain import { ApiPromise, WsProvider } from '@polkadot/api'; const abi = JSON.parse(fs.readFileSync('./RelayChainDataVerifierABI.json')); const privateKey = 'INSERT_PRIVATE_KEY'; const precompileAddress = '0x0000000000000000000000000000000000000819'; const moonbeamURL = 'https://rpc.api.moonbase.moonbeam.network'; const relayURL = 'wss://relay.api.moonbase.moonbeam.network'; // Create Web3js provider and signer const web3 = new Web3(moonbeamURL); const precompileContract = new web3.eth.Contract(abi, precompileAddress); const account = web3.eth.accounts.privateKeyToAccount(privateKey); async function run() { // Create provider for relay chain const wsProvider = new WsProvider(relayURL); const api = await ApiPromise.create({ provider: wsProvider }); // Get the storage key for a random account on relay chain const key = api.query.system.account.key( '5CBATpb3yvEM4mhX9Dw3tyuqiWKhq9YBG6ugSbodRUSbodoU' ); // Find the latest available relay chain block number from Moonbeam const blockNum = await precompileContract.methods .latestRelayBlockNumber() .call(); // Get the block hash and storage proof from relay chain const blockHash = await api.rpc.chain.getBlockHash(blockNum); const proof = await api.rpc.state.getReadProof([key], blockHash); const callObject = { to: precompileAddress, data: precompileContract.methods .verifyEntry(blockNum, proof, key) .encodeABI(), gas: await precompileContract.methods .verifyEntry(blockNum, proof, key) .estimateGas(), gasPrice: await web3.eth.getGasPrice(), nonce: await web3.eth.getTransactionCount(account.address), }; // This tx will be rejected if the verification failed const tx = await web3.eth.accounts.signTransaction( callObject, account.privateKey ); const receipt = await web3.eth.sendSignedTransaction(tx.rawTransaction); console.log(receipt.transactionHash); } await run(); ``` === "Web3.py" ```py # Import packages from eth_account import Account from substrateinterface import SubstrateInterface from web3 import Web3 # Initialize variables abi = INSERT_ABI privateKey = "INSERT_PRIVATE_KEY" precompileAddress = "0x0000000000000000000000000000000000000819" moonbeamURL = "https://rpc.api.moonbase.moonbeam.network" relayURL = "wss://relay.api.moonbase.moonbeam.network" # Create provider for Moonbeam network web3 = Web3(Web3.HTTPProvider(moonbeamURL)) account = Account.from_key(privateKey) precompileContract = web3.eth.contract(address=precompileAddress, abi=abi) # Create provider for relay chain substrate = SubstrateInterface(url=relayURL) # Get storage key key = substrate.generate_storage_hash( storage_module="System", storage_function="Account", params=["5CBATpb3yvEM4mhX9Dw3tyuqiWKhq9YBG6ugSbodRUSbodoU"], ) # Find the latest available relay chain block number from Moonbeam blockNum = precompileContract.functions.latestRelayBlockNumber().call() # Get the block hash from relay chain blockHash = substrate.get_block_hash(blockNum) # Get the storage proof from relay chain response = substrate.rpc_request("state_getReadProof", [[key], blockHash]) proof = response["result"] # Call smart contract tx = precompileContract.functions.verifyEntry(blockNum, proof, key).build_transaction( { "from": Web3.to_checksum_address(account.address), "nonce": web3.eth.get_transaction_count( Web3.to_checksum_address(account.address) ), } ) tx_create = web3.eth.account.sign_transaction(tx, privateKey) tx_hash = web3.eth.send_raw_transaction(tx_create.rawTransaction) tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash) ``` --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/ux/batch/ --- BEGIN CONTENT --- --- title: Batch Precompile Contract description: Learn how to transact multiple transfers and contract interactions at once via a Solidity interface with Moonbeam's Batch Precompile contract. keywords: solidity, ethereum, batch, transaction, moonbeam, precompiled, contracts categories: Precompiles, Ethereum Toolkit --- # Interacting with the Batch Precompile ## Introduction {: #introduction } The batch precompiled contract on Moonbeam allows developers to combine multiple EVM calls into one. Currently, having users interact with multiple contracts would require multiple transaction confirmations in the user's wallet. An example would be approving a smart contract's access to a token, then transferring it. With the batch precompile, developers can enhance user experience with batched transactions as it minimizes the number of transactions a user is required to confirm to one. Additionally, gas fees can be reduced since batching avoids multiple base gas fees (the initial 21000 units of gas spent to begin a transaction). The precompile interacts directly with [Substrate's EVM pallet](/learn/platform/technology/#evm-pallet){target=\_blank}. The caller of the batch function will have their address act as the `msg.sender` for all subtransactions, but unlike [delegate calls](https://docs.soliditylang.org/en/v0.8.15/introduction-to-smart-contracts.html#delegatecall-callcode-and-libraries){target=\_blank}, the target contract will still affect its own storage. It is effectively the same as if the user signed multiple transactions, but with only one confirmation. The precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.batch }} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.batch }} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.batch }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Batch Solidity Interface {: #the-batch-interface } [`Batch.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/batch/Batch.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's three methods. The interface includes the following functions: ??? function "**batchSome**(*address[]* to, *uint256[]* value, *bytes[]* callData, *uint64[]* gasLimit) - performs multiple calls, where the same index of each array combine into the information required for a single subcall. If a subcall reverts, following subcalls will still be attempted" === "Parameters" - `to` - address[] array of addresses to direct subtransactions to, where each entry is a subtransaction - `value` - uint256[] array of native currency values to send in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. If this array is shorter than the to array, all the following subtransactions will default to a value of 0 - `callData` - bytes[] array of call data to include in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. If this array is shorter than the to array, all of the following subtransactions will include no call data - `gasLimit` - uint64[] array of gas limits in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. Values of 0 are interpreted as unlimited and will have all remaining gas of the batch transaction forwarded. If this array is shorter than the to array, all of the following subtransactions will have all remaining gas forwarded ??? function "**batchSomeUntilFailure**(*address[]* to, *uint256[]* value, *bytes[]* callData, *uint64[]* gasLimit) - performs multiple calls, where the same index of each array combine into the information required for a single subcall. If a subcall reverts, no following subcalls will be executed" === "Parameters" - `to` - address[] array of addresses to direct subtransactions to, where each entry is a subtransaction - `value` - uint256[] array of native currency values to send in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. If this array is shorter than the to array, all the following subtransactions will default to a value of 0 - `callData` - bytes[] array of call data to include in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. If this array is shorter than the to array, all of the following subtransactions will include no call data - `gasLimit` - uint64[] array of gas limits in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. Values of 0 are interpreted as unlimited and will have all remaining gas of the batch transaction forwarded. If this array is shorter than the to array, all of the following subtransactions will have all remaining gas forwarded ??? function "**batchAll**(*address[]* to, *uint256[]* value, *bytes[]* callData, *uint64[]* gasLimit) - performs multiple calls atomically, where the same index of each array combine into the information required for a single subcall. If a subcall reverts, all subcalls will revert" === "Parameters" - `to` - address[] array of addresses to direct subtransactions to, where each entry is a subtransaction - `value` - uint256[] array of native currency values to send in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. If this array is shorter than the to array, all the following subtransactions will default to a value of 0 - `callData` - bytes[] array of call data to include in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. If this array is shorter than the to array, all of the following subtransactions will include no call data - `gasLimit` - uint64[] array of gas limits in the subtransactions, where the index corresponds to the subtransaction of the same index in the to array. Values of 0 are interpreted as unlimited and will have all remaining gas of the batch transaction forwarded. If this array is shorter than the to array, all of the following subtransactions will have all remaining gas forwarded The interface also includes the following required events: - **SubcallSucceeded**(*uint256* index) - emitted when subcall of the given index succeeds - **SubcallFailed**(*uint256* index) - emitted when a subcall of the given index fails ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } ### Checking Prerequisites {: #checking-prerequisites } To follow along with this tutorial, you will need to have: - [MetaMask installed and connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - Create or have two accounts on Moonbase Alpha to test out the different features in the batch precompile - At least one of the accounts will need to be funded with `DEV` tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Example Contract {: #example-contract} The contract `SimpleContract.sol` will be used as an example of batching contract interactions, but in practice, any contract can be interacted with. ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; contract SimpleContract { mapping(uint256 => string) public messages; function setMessage(uint256 id, string calldata message) external { messages[id] = message; } } ``` ### Remix Set Up {: #remix-set-up } You can interact with the batch precompile using [Remix](https://remix.ethereum.org){target=\_blank}. You'll need a copy of [`Batch.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/batch/Batch.sol){target=\_blank} and [`SimpleContract.sol`](#example-contract). To add the precompile to Remix and follow along with the tutorial, you will need to: 1. Click on the **File explorer** tab 2. Paste the `Batch.sol` contract into a Remix file named **Batch.sol** 3. Paste the `SimpleContract.sol` contract into a Remix file named **SimpleContract.sol** ### Compile the Contract {: #compile-the-contract } Next, you will need to compile both files in Remix: 1. Make sure that you have the **Batch.sol** file open 2. Click on the **Compile** tab, second from top 3. To compile the contract, click on **Compile Batch.sol** ![Compiling Batch.sol](/images/builders/ethereum/precompiles/ux/batch/batch-1.webp) If the interface was compiled successfully, you will see a green checkmark next to the **Compile** tab. ### Access the Precompile {: #access-the-precompile } Instead of deploying the batch precompile, you will access the interface given the address of the precompiled contract: 1. Click on the **Deploy and Run** tab directly below the **Compile** tab in Remix. Please note the precompiled contract is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** dropdown. Once you select **Injected Provider - Metamask**, you might be prompted by MetaMask to connect your account to Remix 3. Make sure the correct account is displayed under **ACCOUNT** 4. Ensure **Batch - Batch.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract, there is no need to deploy any code. Instead we are going to provide the address of the precompile in the **At Address** field 5. Provide the address of the batch precompile: `{{networks.moonbase.precompiles.batch}}` and click **At Address** ![Access the address](/images/builders/ethereum/precompiles/ux/batch/batch-2.webp) The **BATCH** precompile will appear in the list of **Deployed Contracts**. ### Deploy Example Contract {: #deploy-example-contract } On the other hand, `SimpleContract.sol` will be deployed as a new contract. Before starting this section, repeat the [compilation step](#compile-the-contract) with the `SimpleContract.sol` file. 1. Click on the **Deploy and Run** tab directly below the **Compile** tab in Remix 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** dropdown. Once you select **Injected Provider - Metamask**, you might be prompted by MetaMask to connect your account to Remix 3. Make sure the correct account is displayed under **ACCOUNT** 4. Ensure **SimpleContract - SimpleContract.sol** is selected in the **CONTRACT** dropdown 5. Click **Deploy** 6. Confirm the MetaMask transaction that appears by clicking **Confirm** ![Deploy SimpleContract](/images/builders/ethereum/precompiles/ux/batch/batch-3.webp) The **SIMPLECONTRACT** contract will appear in the list of **Deployed Contracts**. ### Send Native Currency via Precompile {: #send-native-currency-via-precompile } Sending native currency with the batch precompile is more involved than pressing a few buttons in Remix or MetaMask. For this example, you will be using the **batchAll** function to send native currency atomically. Transactions have a value field to specify the amount of native currency being sent with it. In Remix, this is represented by the **VALUE** input in the **DEPLOY & RUN TRANSACTIONS** tab. However, for the batch precompile, this data is provided within the **value** array input of the batch functions. Try transferring native currency to two wallets of your choice via the batch precompile on Moonbase Alpha: 1. Make sure that you have at least 0.5 DEV in your connected wallet 2. Expand the batch contract under **Deployed Contracts** 3. Expand the **batchAll** function 4. For the **to** input, insert your addresses in the following format: `["INSERT_ADDRESS_1", "INSERT_ADDRESS_2"]`, where the first address corresponds to the first wallet of your choice and the second address corresponds to the second wallet of your choice 5. For the **value** input, insert the amount you wish to transfer in Wei for each address. For example, `["100000000000000000", "200000000000000000"]` will transfer 0.1 DEV to the first address and 0.2 DEV to the second address 6. For both of the remaining **callData** and **gasLimit** inputs, insert `[]`. Call data and gas limit are not a concern for transferring native currency 7. Press **transact** 8. Press **Confirm** in the MetaMask extension to confirm the transaction ![Send Batch Transfer](/images/builders/ethereum/precompiles/ux/batch/batch-4.webp) Once the transaction is complete, be sure to check both of the accounts' balances, either in MetaMask or in a [block explorer](/builders/get-started/explorers/){target=\_blank}. Congratulations! You've now sent a batched transfer via the batch precompile. !!! note Typically if you wanted to send the native currency to or through a contract, you would have to set the value within the overall transaction object and interact with a payable function. However, since the batch precompile interacts directly with Substrate code, this is not a typical Ethereum transaction and is thus not necessary. ### Find a Contract Interaction's Call Data {: #find-a-contract-interactions-call-data } Visual interfaces like [Remix](/builders/ethereum/dev-env/remix/){target=\_blank} and handy libraries like [Ethers.js](/builders/ethereum/libraries/ethersjs/){target=\_blank} hide the way that Ethereum transactions interact with Solidity smart contracts. The name and input types of a function are hashed into a [function selector](https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector-and-argument-encoding){target=\_blank} and the input data is encoded. These two pieces are then combined and sent as the transaction's call data. To send a subtransaction within a batch transaction, the sender needs to know its call data beforehand. Try finding a transaction's call data using Remix: 1. Expand the `SimpleContract.sol` contract under **Deployed Contracts** 2. Expand the **setMessage** function 3. Enter the input of the function. For this example, **id** will be `1` and **message** will be `"moonbeam"` 4. Instead of sending the transaction, click the copy button next to the **transact** button to copy the call data ![Transaction Call Data](/images/builders/ethereum/precompiles/ux/batch/batch-5.webp) Now you have the transaction's call data! Considering the example values of `1` and `"moonbeam"`, we can keep an eye out for their encoded values in the call data: ```text 0x648345c8 // function selector 0000000000000000000000000000000000000000000000000000000000000001 // 1 id 0000000000000000000000000000000000000000000000000000000000000040 // 64 string offset 0000000000000000000000000000000000000000000000000000000000000008 // 8 length in bytes 6d6f6f6e6265616d000000000000000000000000000000000000000000000000 // "moonbeam" in bytes ``` The call data can be broken into five lines, where: - The first line is the function selector - The second line is equal to 1, which is the **id** that was provided - What's left has to do with the **message** input. These last three lines are tricky, since strings are a [dynamic type](https://docs.soliditylang.org/en/v0.8.15/abi-spec.html#use-of-dynamic-types){target=\_blank} with a dynamic length. The third line refers to an offset to define where the string's data starts. The fourth line refers to the string's length, in this case 8 because "moonbeam" is 8 bytes long . Finally, the fifth line is "moonbeam" in hexadecimal format (8 ASCII characters are 16 hexadecimal characters) left aligned and with zeros for padding ### Function Interaction via Precompile {: #function-interaction-via-precompile } This section's example will be using the **batchAll** function that will ensure the transactions are resolved atomically. Keep in mind that there are also two other batch functions that can either continue subtransactions despite errors or halt subsequent subtransactions but not revert previous ones. Interacting with a function is very similar to [sending a native currency](#send-native-currency-via-precompile), since they are both transactions. However, call data is required to properly provide input to functions and a sender may desire to limit the amount of gas spent in each subtransaction. The `callData` and `gasLimit` fields are more relevant for subtransactions that interact with contracts. For each function in the batch interface, the `callData` input is an array where each index corresponds to the call data for each recipient of the subtransaction, that is, each `to` input. If the size of the `callData` array is less than the `to` array, the remaining subtransactions will have no call data (functions with no inputs). The `gasLimit` input is an array that corresponds to the amount of gas that each can spend for each subtransaction. If its value at an index is 0 or the index is the size of the array or greater (and smaller than the `to` array's size), all of the remaining gas from the previous subtransaction is forwarded. To use the precompile to send an atomic batch transaction, take the following steps: 1. Copy the `SimpleContract.sol` contract's address with the copy button on the right side of its header. Be sure to also have the [call data from the previous section](#find-a-contract-interactions-call-data) 2. Expand the batch contract under **Deployed Contracts** 3. Expand the **batchAll** function 4. For the **to** input, insert the address of the `SimpleContract.sol` contract that you previously copied in the following format: `["INSERT_SIMPLE_CONTRACT_ADDRESS"]` 5. For the value input, since `SimpleContract.sol` does not require any native currency to be paid to it, insert `["0"]` for 0 Wei 6. For the **callData** input, insert your call data from the previous section in the following format: `["INSERT_CALL_DATA"]` 7. For the **gasLimit** input, insert `[]`. You can put in a gas limit value, but it is optional 8. Press **transact** 9. Press **Confirm** in the MetaMask extension to confirm the transaction ![Batch Function Interaction](/images/builders/ethereum/precompiles/ux/batch/batch-6.webp) If you used the same call data as the tutorial, check to make sure that the transaction has been successful: 1. Expand the `SimpleContract.sol` contract under **Deployed Contracts** 2. To the right of the **messages** button, insert `1` 3. Press the blue **messages** button ![SimpleContract Confirmation](/images/builders/ethereum/precompiles/ux/batch/batch-7.webp) The phrase **"moonbeam"** should appear underneath it. Congratulations! You have interacted with a function with the batch precompile. ### Combining Subtransactions {: combining-subtransactions } So far, transferring native currency and interacting with functions have been separate, but they can be intertwined. The following four strings can be combined as inputs for a batch transaction. They will transact 1 DEV to the public Gerald (`0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b`) account, and interact with a predeployed `SimpleContract.sol` contract twice. Here is a break-down: There are three subtransactions, so there are three addresses in the `to` input array. The first is the public Gerald account, the next two are a predeployed `SimpleContract.sol` contract. You can replace the last two with your own instance of `SimpleContract.sol` if you wish. Or, replace only one: you can interact with multiple contracts in a single message. ```text [ "0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b", "0xd14b70a55F6cBAc06d4FA49b99be0370D0e1BD39", "0xd14b70a55F6cBAc06d4FA49b99be0370D0e1BD39" ] ``` There will also be three values for the `value` array. The first address in the `to` input array has to do with sending 1 DEV, so 1 DEV in Wei is within the array. The following two values are 0 because the function that their subtransactions are interacting with do not accept or require native currency. ```text ["1000000000000000000", "0", "0"] ``` You will need three values for the `callData` array. Since transferring native currency does not require call data, the string is simply blank. The second and third values in the array correspond to invocations of **setMessage** that set messages to ids 5 and 6. ```text [ "0x", "0x648345c8000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000009796f752061726520610000000000000000000000000000000000000000000000", "0x648345c800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000e61206d6f6f6e6265616d2070726f000000000000000000000000000000000000" ] ``` The final input is for `gas_input`. This array will be left empty to forward all remaining gas to each subtransaction. ```text [] ``` Try sending a batched transaction with these inputs in Remix the same way [you batched a function call](#function-interaction-via-precompile). And that's it! You've successfully interacted with the ERC-20 precompile using MetaMask and Remix! ## Ethereum Development Libraries {: #ethereum-development-libraries } If you have followed the [Ethers.js tutorial](/builders/ethereum/libraries/ethersjs/){target=\_blank} on Moonbeam, you may find it difficult to find the call data for a function. The answer is hidden within Ether's `Interface` object, where the [encodeFunctionData](https://docs.ethers.org/v6/api/abi/#Interface-encodeFunctionData){target=\_blank} function allows you to input your function name and inputs to receive the resultant call data. [Web3.js](/builders/ethereum/libraries/web3js/){target=\_blank} has a similar function, [encodeFunctionCall](https://web3js.readthedocs.io/en/v1.2.11/web3-eth-abi.html#encodefunctioncall){target=\_blank}. !!! note The code snippets presented in the following sections are not meant for production environments. Please make sure you adapt it for each use-case. === "Ethers.js" ```js // Import the contract ABI const { abi } = require('./INSERT_ABI_PATH'); // Use ABI to create an interface const yourContractInterface = new ethers.Interface(abi); // Find call data for the setMessage function const callData = yourContractInterface.encodeFunctionData( 'INSERT_FUNCTION_NAME', [ 'INSERT_INPUT_1', 'INSERT_INPUT_2', // ... ] ); ``` === "Web3.js" ```js // Import the contract ABI const { abi } = require('./INSERT_ABI_PATH'); // Find call data for the setMessage function const callData = web3.eth.abi.encodeFunctionCall(abi, [ 'INSERT_INPUT_1', 'INSERT_INPUT_2', // ... ]); ``` === "Web3.py" ```py # Import the ABI and bytecode from compile import abi, bytecode # Create contract instance your_contract = web3.eth.contract(abi=abi, bytecode=bytecode) # Encode the contract call call_data = your_contract.encodeABI( fn_name="INSERT_FUNCTION_NAME", args=["INSERT_INPUT_1", "INSERT_INPUT_2", ...] ) ``` Afterwards, you should be all set to interact with the batch precompile as one typically would with a contract in [Ethers](/builders/ethereum/libraries/ethersjs/){target=\_blank}. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/ux/call-permit/ --- BEGIN CONTENT --- --- title: Call Permit Precompile Contract description: Learn how to use the Call Permit Precompile contract on Moonbeam to sign a permit for any EVM call that can be dispatched by anyone or any smart contract. keywords: solidity, ethereum, call permit, permit, gasless transaction, moonbeam, precompiled, contracts categories: Precompiles, Ethereum Toolkit --- # Interacting with the Call Permit Precompile ## Introduction {: #introduction } The Call Permit Precompile on Moonbeam allows a user to sign a permit, an [EIP-712](https://eips.ethereum.org/EIPS/eip-712){target=\_blank} signed message, for any EVM call and it can be dispatched by anyone or any smart contract. It is similar to the [ERC-20 Permit Solidity Interface](/builders/interoperability/xcm/xc20/interact/#the-erc20-permit-interface){target=\_blank}, except it applies to any EVM call instead of approvals only. When the call permit is dispatched, it is done so on behalf of the user who signed the permit and the user or contract that dispatches the permit is responsible for paying transaction fees. As such, the precompile can be used to perform gas-less transactions. For example, Alice signs a call permit and Bob dispatches it and performs the call on behalf of Alice. Bob pays for the transaction fees and as such, Alice doesn't need to have any of the native currency to pay for the transaction, unless the call includes a transfer. The Call Permit Precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.call_permit }} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.call_permit }} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.call_permit }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The Call Permit Solidity Interface {: #the-call-permit-interface } [`CallPermit.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/call-permit/CallPermit.sol){target=\_blank} is a Solidity interface that allows developers to interact with the precompile's three methods. The interface includes the following functions: ??? function "**dispatch**(*address* from, *address* to, *uint256* value, *bytes* data, *uint64[]* gaslimit, *uint256* deadline, *uint8* v, *bytes32* r, *bytes32* s) - dispatches a call on the behalf of another user with a EIP-712 permit. This function can be called by anyone or any smart contract. The transaction will revert if the permit is not valid or if the dispatched call reverts or errors (such as out of gas). If successful, the nonce of the signer is increased to prevent this permit to be replayed" === "Parameters" - `from` - address of the signer of the permit. The call will be dispatched on behalf of this address - `to` - address the call is made to - `value` - uint256 value being transferred from the `from` account - `data` - bytes containing the call data, or action to be executed - `gasLimit` - uint64[] gas limit the dispatched call requires. Providing an argument for this parameter prevents the dispatcher from manipulating the gas limit - `deadline` - uint256 time in UNIX seconds after which the permit will no longer be valid. In JavaScript, you can get the current time in UNIX seconds by running `console.log(Date.now())` in a JavaScript script or a browser console - `v` - uint8 recovery ID of the signature. The last one byte of the concatenated signature - `r` - bytes32 first 32 bytes of the concatenated signature - `s` - bytes32 second 32 bytes of the concatenated signature ??? function "**nonces**(*address* owner) - returns the current nonce for given owner" === "Parameters" - `owner` - address of the account to query the nonce for ??? function "**DOMAIN_SEPARATOR**() - returns the EIP-712 domain separator which is used to avoid replay attacks. It follows the [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612#specification){target=\_blank} implementation" === "Parameters" None. The domain separator is defined in the [EIP-712 standard](https://eips.ethereum.org/EIPS/eip-712){target=\_blank} and is calculated as: ```text keccak256(PERMIT_DOMAIN, name, version, chain_id, address) ``` The parameters of the hash can be broken down as follows: - **PERMIT_DOMAIN** - is the `keccak256` of `EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)` - **name** - is the name of the signing domain and must be `'Call Permit Precompile'` exactly - **version** - is the version of the signing domain. For this case **version** is set to `1` - **chainId** - is the chain ID of the network - **verifyingContract** - is the address of the contract that will verify the signature. In this case, the Call Permit Precompile address When `dispatch` is called, the permit needs to be verified before the call is dispatched. The first step is to [compute the domain separator](https://github.com/moonbeam-foundation/moonbeam/blob/ae705bb2e9652204ace66c598a00dcd92445eb81/precompiles/call-permit/src/lib.rs#L138){target=\_blank}. The calculation can be seen in [Moonbeam's implementation](https://github.com/moonbeam-foundation/moonbeam/blob/ae705bb2e9652204ace66c598a00dcd92445eb81/precompiles/call-permit/src/lib.rs#L112-L126){target=\_blank} or you can check out a practical example in [OpenZeppelin's EIP712 contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4a9cc8b4918ef3736229a5cc5a310bdc17bf759f/contracts/utils/cryptography/draft-EIP712.sol#L70-L84){target=\_blank}. From there, a [hash of the signature and the given arguments](https://github.com/moonbeam-foundation/moonbeam/blob/ae705bb2e9652204ace66c598a00dcd92445eb81/precompiles/call-permit/src/lib.rs#L140-L151){target=\_blank} is generated which guarantees that the signature can only be used for the call permit. It uses a given nonce to ensure the signature is not subject to a replay attack. It is similar to [OpenZeppelin's `ERC20Permit` contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4a9cc8b4918ef3736229a5cc5a310bdc17bf759f/contracts/token/ERC20/extensions/draft-ERC20Permit.sol#L52){target=\_blank}, except the `PERMIT_TYPEHASH` is for a call permit, and the arguments match that of the [dispatch function](#:~:text=The interface includes the following functions) plus the nonce. The domain separator and the hash struct can be used to build the [final hash](https://github.com/moonbeam-foundation/moonbeam/blob/ae705bb2e9652204ace66c598a00dcd92445eb81/precompiles/call-permit/src/lib.rs#L153-L157){target=\_blank} of the fully encoded message. A practical example is shown in [OpenZeppelin's EIP712 contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4a9cc8b4918ef3736229a5cc5a310bdc17bf759f/contracts/utils/cryptography/draft-EIP712.sol#L101){target=\_blank}. With the final hash and the v, r, and s values, the signature can be [verified and recovered](https://github.com/moonbeam-foundation/moonbeam/blob/ae705bb2e9652204ace66c598a00dcd92445eb81/precompiles/call-permit/src/lib.rs#L211-L223){target=\_blank}. If successfully verified, the nonce will increase by one and the call will be dispatched. ## Setup the Contracts {: #setup-the-example-contract } For this example, you'll learn how to sign a call permit that updates a message in a simple example contract, [`SetMessage.sol`](#example-contract). Before you can generate the call permit signature, you'll need to deploy the contract and define the `dispatch` function arguments for the call permit. Once you've setup the example contract, then you can setup the Call Permit Precompile contract. ### Checking Prerequisites {: #checking-prerequisites } To follow along with this tutorial, you will need to have: - [MetaMask installed and connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - Create or have two accounts on Moonbase Alpha to test out the different features in the Call Permit Precompile - At least one of the accounts will need to be funded with `DEV` tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Example Contract {: #example-contract } The `SetMessage.sol` contract will be used as an example of using a call permit, but in practice, any contract can be interacted with. ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.7; contract SetMessage { string storedMessage; function set(string calldata x) public { storedMessage = x; } function get() public view returns (string memory) { return storedMessage; } } ``` ### Remix Set Up {: #remix-set-up } You can use [Remix](https://remix.ethereum.org){target=\_blank} to compile the example contract and deploy it. You'll need a copy of [`SetMessage.sol`](#example-contract){target=\_blank} and [`CallPermit.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/call-permit/CallPermit.sol){target=\_blank}. To add the contracts to Remix, you can take the following steps: 1. Click on the **File explorer** tab 2. Paste the `SetMessage.sol` contract into a Remix file named `SetMessage.sol` 3. Paste the `CallPermit.sol` contract into a Remix file named `CallPermit.sol` ![Copying and pasting the example contract into Remix](/images/builders/ethereum/precompiles/ux/call-permit/call-1-new.webp) ### Compile & Deploy the Example Contract {: #compile-deploy-example-contract } First you'll need to compile the example contract: 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile SetMessage.sol** ![Compiling SetMessage.sol](/images/builders/ethereum/precompiles/ux/call-permit/call-2.webp) Then you can deploy it: 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **SetMessage.sol** is selected in the **CONTRACT** dropdown 4. Click **Deploy** 4. MetaMask will pop up and you'll need to **Confirm** the transaction ![Provide the address](/images/builders/ethereum/precompiles/ux/call-permit/call-3.webp) The contract will appear under the list of **Deployed Contracts** on the left side panel. Copy the contract address as you will need to use it to generate the call permit signature in the next section. ### Compile & Access the Call Permit Precompile {: #compile-access-call-permit } First you'll need to compile the Call Permit Precompile contract: 1. Click on the **Compile** tab, second from top 2. Then to compile the interface, click on **Compile CallPermit.sol** ![Compiling SetMessage.sol](/images/builders/ethereum/precompiles/ux/call-permit/call-4.webp) Then instead of deploying the contract, you'll just need to access it given the address of the precompile: 1. Click on the **Deploy and Run** tab, directly below the **Compile** tab in Remix. Note: you are not deploying a contract here, instead you are accessing a precompiled contract that is already deployed 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** drop down 3. Ensure **CallPermit.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract there is no need to deploy, instead you are going to provide the address of the precompile in the **At Address** field 4. Provide the address of the Call Permit Precompile for Moonbase Alpha: `{{networks.moonbase.precompiles.call_permit}}` and click **At Address** 5. The Call Permit Precompile will appear in the list of **Deployed Contracts** ![Provide the address](/images/builders/ethereum/precompiles/ux/call-permit/call-5.webp) ## Generate Call Permit Signature {: #generate-call-permit-signature} In order to interact with the Call Permit Precompile, you have to have or generate a signature to dispatch the call permit with. There are several ways you can generate the signature, this guide will show you two different ways to generate it: in the browser using the [MetaMask extension](https://chromewebstore.google.com/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn){target=\_blank} and [JSFiddle](https://jsfiddle.net){target=\_blank} and using MetaMask's [`@metamask/eth-sig-util` npm package](https://www.npmjs.com/package/@metamask/eth-sig-util){target=\_blank}. Regardless of which method you choose to generate the signature, the following steps will be taken: 1. The `message` will be created and includes some of the data that is needed to create the call permit. It includes the arguments that will be passed into the `dispatch` function and the nonce of the signer 2. A JSON structure of the data the user needs to sign will be assembled for the call permit and include all of the types for the `dispatch` arguments and the nonce. This will result in the `CallPermit` type and will be saved as the `primaryType` 3. The domain separator will be created using `"Call Permit Precompile"` exactly for the name, the version of your DApp or platform, the chain ID of the network the signature is to be used on, and the address of the contract that will verify the signature 4. All of the assembled data, the `types`, `domain`, `primaryType` and `message`, will be signed using MetaMask (either in the browser or through the MetaMask's JavaScript signing library) 5. The signature will be returned and you can use [Ethers.js](https://docs.ethers.org/v6){target=\_blank} [`Signature.from` method](https://docs.ethers.org/v6/api/crypto/#Signature_from){target=\_blank} to return the `v`, `r`, and `s` values of the signature ### The Call Permit Arguments {: #call-permit-arguments } As seen in the [Call Permit Interface](#the-call-permit-interface) section, the `dispatch` function takes the following parameters: `from`, `to`, `value`, `data`, `gasLimit`, `deadline`, `v`, `r`, and `s`. In order to get the signature arguments (`v`, `r`, and `s`), you'll need to sign a message containing the arguments for the remainder of the aforementioned parameters, plus the nonce of the signer. - `from` - the address of the account you want to sign the call permit with - `to` - the contract address for the `SetMessage.sol` contract - `value` - can be `0` for this example as you'll just be setting a message instead of transferring any funds - `data` - you can send any message you would like, you'll just need the hex representation of the message you want to set using the `SetMessage.sol` contract. This will contain the function selector of the `set` function and the string of the message. For this example, you can send `hello world`. To do so, you can use this hex representation: ```text 0x4ed3885e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000 ``` - `gasLimit` - `100000` will be enough to send the dispatched call - `deadline` - you can get the current time in UNIX seconds by running `console.log(Date.now())` in a JavaScript script or a browser console. Once you have the current time, you can add additional time in seconds to represent when the call permit will expire The nonce of the signer will also be needed. If this is your first time signing a call permit the nonce will be `0`. You can also check the nonce in Remix: 1. Expand the call permit contract 2. Next to the **nonces** function, enter the address of the signer and click on **nonces** 3. The result will be returned directly under the function ![Get the nonce](/images/builders/ethereum/precompiles/ux/call-permit/call-6.webp) ### Use the Browser {: #use-the-browser } To get started, you can open [JSFiddle](https://jsfiddle.net){target=\_blank} or another JavaScript playground in the browser. First, you'll need to add [Ethers.js](/builders/ethereum/libraries/ethersjs/){target=\_blank} as it will be used to get the `v`, `r`, and `s` values of the signature: 1. Click on **Resources** 2. Start to type in `ethers` and the dropdown should populate matching libraries. Choose **ethers** 3. Click on the **+** button The CDN for Ethers.js will appear in the list of libraries under **Resources**. ![Add Ethers to JSFiddle](/images/builders/ethereum/precompiles/ux/call-permit/call-7.webp) In the **Javascript** code box, copy and paste the following JavaScript snippet, making sure to replace the `to` variables (and any other variables as you see fit): ```js const main = async () => { await window.ethereum.enable(); const accounts = await window.ethereum.request({ method: 'eth_requestAccounts', }); const from = accounts[0]; const to = 'INSERT_TO_ADDRESS'; const value = 0; const data = '0x4ed3885e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000'; const gaslimit = 100000; const nonce = 'INSERT_SIGNERS_NONCE'; const deadline = 'INSERT_DEADLINE'; const createPermitMessageData = function () { const message = { from: from, to: to, value: value, data: data, gaslimit: gaslimit, nonce: nonce, deadline: deadline, }; const typedData = JSON.stringify({ types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'chainId', type: 'uint256', }, { name: 'verifyingContract', type: 'address', }, ], CallPermit: [ { name: 'from', type: 'address', }, { name: 'to', type: 'address', }, { name: 'value', type: 'uint256', }, { name: 'data', type: 'bytes', }, { name: 'gaslimit', type: 'uint64', }, { name: 'nonce', type: 'uint256', }, { name: 'deadline', type: 'uint256', }, ], }, primaryType: 'CallPermit', domain: { name: 'Call Permit Precompile', version: '1', chainId: 1287, verifyingContract: '0x000000000000000000000000000000000000080a', }, message: message, }); return { typedData, message, }; }; const method = 'eth_signTypedData_v4'; const messageData = createPermitMessageData(); const params = [from, messageData.typedData]; web3.currentProvider.sendAsync( { method, params, from, }, function (err, result) { if (err) return console.dir(err); if (result.error) { alert(result.error.message); return console.error('ERROR', result); } console.log('Signature:' + JSON.stringify(result.result)); const ethersSignature = ethers.Signature.from(result.result); const formattedSignature = { r: ethersSignature.r, s: ethersSignature.s, v: ethersSignature.v, }; console.log(formattedSignature); } ); }; main(); ``` To run the code, click **Run** at the top of the page (or you can also use `control` and `s`). MetaMask should pop up and prompt you to connect an account. Make sure to choose the account you want to sign the message with. Then go ahead and sign the message. ![Sign the message with MetaMask](/images/builders/ethereum/precompiles/ux/call-permit/call-8.webp) Once you've signed the message, go back to JSFiddle and if the console isn't already open, go ahead and open it to see the signature values include the `v`, `r`, and `s`, values. Copy these values as you'll need them when interacting with the Call Permit Precompile in the following sections. ![Signature values in the JSFiddle console](/images/builders/ethereum/precompiles/ux/call-permit/call-9.webp) ### Use MetaMask's JS Signing Library {: #use-metamasks-signing-library } To generate the call permit signature using JavaScript and MetaMask's [`@metamask/eth-sig-util` npm package](https://www.npmjs.com/package/@metamask/eth-sig-util){target=\_blank}, you'll first need to create a project locally. You can do so with the following commands: ```bash mkdir call-permit-example && cd call-permit-example && touch getSignature.js npm init -y ``` You should now have a file where you can create the script to get the signature along with a `package.json` file. Open the `package.json` file, and below the `"dependencies"` section, add: ```json "type": "module" ``` Next, you can install the MetaMask signing library and [Ethers.js](https://docs.ethers.org/v6){target=\_blank}: ```bash npm i @metamask/eth-sig-util ethers ``` !!! note Never reveal your private keys as they give direct access to your funds. The following steps are for demonstration purposes only. In the `getSignature.js` file, you can copy the following code snippet: ```js import { ethers } from 'ethers'; import { signTypedData, SignTypedDataVersion } from '@metamask/eth-sig-util'; const from = 'INSERT_FROM_ADDRESS'; const to = 'INSERT_TO_ADDRESS'; const value = 0; const data = '0x4ed3885e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000'; const gaslimit = 100000; const nonce = 'INSERT_SIGNERS_NONCE'; const deadline = 'INSERT_DEADLINE'; const createPermitMessageData = () => { const message = { from: from, to: to, value: value, data: data, gaslimit: gaslimit, nonce: nonce, deadline: deadline, }; const typedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], CallPermit: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'data', type: 'bytes' }, { name: 'gaslimit', type: 'uint64' }, { name: 'nonce', type: 'uint256' }, { name: 'deadline', type: 'uint256' }, ], }, primaryType: 'CallPermit', domain: { name: 'Call Permit Precompile', version: '1', chainId: 1287, verifyingContract: '0x000000000000000000000000000000000000080a', }, message: message, }; return { typedData, message, }; }; const messageData = createPermitMessageData(); // For demo purposes only. Never store your private key in a JavaScript/TypeScript file const signature = signTypedData({ privateKey: Buffer.from('INSERT_FROM_ACCOUNT_PRIVATE_KEY', 'hex'), data: messageData.typedData, version: SignTypedDataVersion.V4, }); console.log(`Transaction successful with hash: ${signature}`); const ethersSignature = ethers.Signature.from(signature); const formattedSignature = { r: ethersSignature.r, s: ethersSignature.s, v: ethersSignature.v, }; console.log(formattedSignature); ``` To run the script, use the following command: ```bash node getSignature.js ``` In the console, you should see the concatenated signature along with the values for the signature including the `v`, `r`, and `s` values. Copy these values as you'll need them when interacting with the Call Permit Precompile in the following sections. ![Signature values in the console](/images/builders/ethereum/precompiles/ux/call-permit/call-10.webp) ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } Now that you have generated the call permit signature you will be able to test out calling the `dispatch` function of the Call Permit Precompile. ### Dispatch a Call {: #dispatch-a-call } When you send the `dispatch` function, you'll need the same arguments as you used to sign the call permit. To get started, go back to the **Deploy and Run** tab in Remix and under the **Deployed Contracts** section expand the call permit contract. Make sure that you're connected to the account that you want to consume the call permit and pay the transaction fees with. Then take the following steps: 1. For the **from** field, enter the account address you used to sign the call permit with 2. Copy and paste the contract address of `SetMessage.sol` 3. Enter `0` for the **value** field 4. Enter the hex representation of the function selector for the `set` function and the string you want to set as the message for the `SetMessage.sol` contract. For this example, `hello world` can be used: ```text 0x4ed3885e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b68656c6c6f20776f726c64000000000000000000000000000000000000000000 ``` 5. Enter `100000` for the **gasLimit** field 6. Enter the `deadline` you used when signing the call permit 7. Copy the `v` value you should have retrieved while generating the call permit signature and paste it into the **v** field 8. Copy the `r` value you should have retrieved while generating the call permit signature and paste it into the **r** field 9. Copy the `s` value you should have retrieved while generating the call permit signature and paste it into the **s** field 10. Click **transact** to send the transaction 11. MetaMask should pop-up and you can confirm the transaction ![Dispatch the call permit](/images/builders/ethereum/precompiles/ux/call-permit/call-11.webp) Once the transaction goes through, you can verify that the message was updated to `hello world`. To do so, you can: 1. Expand the `SetMessage.sol` contract 2. Click on **get** 3. The result will appear below the function, and it should show `hello world` ![Verify the dispatch was executed as intended](/images/builders/ethereum/precompiles/ux/call-permit/call-12.webp) Congratulations! You've successfully generated a call permit signature and used it to dispatch a call on behalf of the call permit signer. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/ethereum/precompiles/ux/erc20/ --- BEGIN CONTENT --- --- title: Native Token ERC-20 Precompile description: Learn how to access and interact with an ERC-20 representation of the native token on Moonbeam through the precompiled ERC-20 Interface. keywords: solidity, ethereum, native, token, moonbeam, precompiled, contracts categories: Precompiles, Ethereum Toolkit --- # Native Token ERC-20 Precompile ## Introduction {: #introduction } The native token ERC-20 precompiled contract on Moonbeam allows developers to interact with the native protocol token through an ERC-20 interface. Although GLMR and MOVR are not ERC-20 tokens, now you can interact with them as if they were native ERC-20s! One of the main benefits of this precompile is that it removes the necessity of having a wrapped representation of the protocol token as an ERC-20 smart contract, such as WETH on Ethereum. Furthermore, it prevents having multiple wrapped representations of the same protocol token. Consequently, DApps that need to interact with the protocol token via an ERC-20 interface can do so without needing a separate smart contract. Under the hood, the [ERC-20 precompile](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/balances-erc20/src/lib.rs){target=\_blank} executes specific Substrate actions related to the Substrate balances pallet, which is coded in Rust. The balances pallet provides functionality for handling the [various types of balances on Moonbeam](/learn/core-concepts/balances/#moonbeam-account-balances){target=\_blank}, setting the free balance, transferring balances, and more. This guide will show you how to interact with DEV tokens, the native protocol tokens for the Moonbase Alpha TestNet, via the ERC-20 precompile. You can also follow and adapt this guide to learn how to use GLMR or MOVR as an ERC-20 token. The precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.erc20 }} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.erc20 }} ``` === "Moonbase Alpha" ```text {{networks.moonriver.precompiles.erc20 }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The ERC-20 Solidity Interface {: #the-erc20-interface } The [`ERC20.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/balances-erc20/ERC20.sol){target=\_blank} interface on Moonbeam follows the [EIP-20 Token Standard](https://eips.ethereum.org/EIPS/eip-20){target=\_blank} which is the standard API interface for tokens within smart contracts. The standard defines the required functions and events that a token contract must implement to be interoperable with different applications. The interface includes the following functions: ??? function "**name**() - read-only function that returns the name of the token" === "Parameters" None. ??? function "**symbol**() - read-only function that returns the symbol of the token" === "Parameters" None. ??? function "**decimals**() - read-only function that returns the decimals of the token" === "Parameters" None. ??? function "**totalSupply**() - read-only function that returns the total number of tokens in existence" === "Parameters" None. ??? function "**balanceOf**(*address* who) - read-only function that returns the balance of the specified address" === "Parameters" - `who` - address of the account to query the balance of ??? function "**allowance**(*address* owner, *address* spender) - read-only function that checks and returns the amount of tokens that a spender is allowed to spend on behalf of the owner" === "Parameters" - `owner` - address of the account that owns the tokens - `spender` - address of the account allowed to spend the tokens ??? function "**transfer**(*address* to, *uint256* value) - transfers a given amount of tokens to a specified address and returns `true` if the transfer was successful" === "Parameters" - `to` - address of the recipient - `value` - uint256 amount of tokens to transfer ??? function "**approve**(*address* spender, *uint256* value) - approves the provided address to spend a specified amount of tokens on behalf of `msg.sender`. Returns `true` if successful" === "Parameters" - `spender` - address to be approved to spend the tokens - `value` - uint256 amount of tokens to be approved for spending ??? function "**transferFrom**(*address* from, *address* to, *uint256* value) - transfers tokens from one given address to another given address and returns `true` if successful" === "Parameters" - `from` - address to transfer tokens from - `to` - address to transfer tokens to - `value` - uint256 amount of tokens to transfer !!! note The ERC-20 standard does not specify the implications of multiple calls to `approve`. Changing an allowance with this function numerous times enables a possible attack vector. To avoid incorrect or unintended transaction ordering, you can first reduce the `spender` allowance to `0` and then set the desired allowance afterward. For more details on the attack vector, you can check out the [ERC-20 API: An Attack Vector on Approve/TransferFrom Methods](https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#){target=\_blank} overview. The interface also includes the following required events: - **Transfer**(*address indexed* from, *address indexed* to, *uint256* value) - emitted when a transfer has been performed - **Approval**(*address indexed* owner, *address indexed* spender, *uint256* value) - emitted when an approval has been registered !!! note The ERC-20 precompile does not include `deposit` and `withdraw` functions and subsequent events that are expected from a wrapped token contract, such as WETH. ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } ### Checking Prerequisites {: #checking-prerequisites } To follow along with this tutorial, you will need to have: - [MetaMask installed and connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} - Create or have two accounts on Moonbase Alpha to test out the different features in the ERC-20 precompile - At least one of the accounts will need to be funded with `DEV` tokens. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} ### Add Token to MetaMask {: #add-token-to-metamask } If you want to interact with Moonbase Alpha DEV tokens like you would with an ERC-20 in MetaMask, you can create a custom token using the precompile address. To get started, open up MetaMask and make sure you are [connected to Moonbase Alpha](/tokens/connect/metamask/){target=\_blank} and: 1. Switch to the **Assets** tab 2. Click on **Import tokens** ![Import Tokens from Assets Tab in MetaMask](/images/builders/ethereum/precompiles/ux/erc20/erc20-1.webp) Now, you can create a custom token: 1. Enter the precompile address for the token contract address - `{{networks.moonbase.precompiles.erc20 }}`. As soon as you enter the address, the **Token Symbol** and **Token Decimal** fields should automatically populate. If they don't you can enter `DEV` for the symbol and `18` for the decimal places 2. Click **Add Custom Token** ![Add Custom Token](/images/builders/ethereum/precompiles/ux/erc20/erc20-2.webp) MetaMask will prompt you to import the tokens. You can review the token details and click **Import Tokens** to import DEV tokens into your wallet. ![Confirm and Import Tokens](/images/builders/ethereum/precompiles/ux/erc20/erc20-3.webp) And that's it! You've successfully added the DEV token as a custom ERC-20 token on the Moonbase Alpha TestNet. ### Remix Set Up {: #remix-set-up } You can interact with the ERC-20 precompile using [Remix](https://remix.ethereum.org){target=\_blank}. To add the precompile to Remix, you will need to: 1. Get a copy of [`ERC20.sol`](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/balances-erc20/ERC20.sol){target=\_blank} 2. Paste the file contents into a Remix file named `IERC20.sol` ### Compile the Contract {: #compile-the-contract } Next, you will need to compile the interface in Remix: 1. Click on the **Compile** tab, second from top 2. Compile the interface by clicking on **Compile IERC20.sol** ![Compiling IERC20.sol](/images/builders/ethereum/precompiles/ux/erc20/erc20-4.webp) If the interface was compiled successfully, you will see a green checkmark next to the **Compile** tab. ### Access the Contract {: #access-the-contract } Instead of deploying the ERC-20 precompile, you will access the interface given the address of the precompiled contract: 1. Click on the **Deploy and Run** tab directly below the **Compile** tab in Remix. Please note the precompiled contract is already deployed 2. Make sure **Injected Web3** is selected in the **ENVIRONMENT** dropdown. Once you select **Injected Web3**, you might be prompted by MetaMask to connect your account to Remix 3. Make sure the correct account is displayed under **ACCOUNT** 4. Ensure **IERC20 - IERC20.sol** is selected in the **CONTRACT** dropdown. Since this is a precompiled contract, there is no need to deploy any code. Instead you are going to provide the address of the precompile in the **At Address** field 5. Provide the address of the ERC-20 precompile: `{{networks.moonbase.precompiles.erc20}}` and click **At Address** ![Access the address](/images/builders/ethereum/precompiles/ux/erc20/erc20-5.webp) The **IERC20** precompile will appear in the list of **Deployed Contracts**. ### Get Basic Token Information {: #get-basic-token-information } The ERC-20 interface allows you to quickly obtain token information, including the token's total supply, name, symbol, and decimal places. You can get this information by following these steps: 1. Expand the **IERC20** contract under **Deployed Contracts** 2. Click **decimals** to get the decimal places of the Moonbase Alpha native protocol token 3. Click **name** to get the name of the token 4. Click **symbol** to get the symbol of the token 5. Click **totalSupply** to obtain the total supply of tokens in existence on Moonbase Alpha ![Total Supply](/images/builders/ethereum/precompiles/ux/erc20/erc20-6.webp) The response for each call will be displayed under the corresponding function. ### Get Account Balance {: #get-account-balance } You can check the balance of any address on Moonbase Alpha by calling the `balanceOf` function and passing in an address: 1. Expand the **balanceOf** function 2. Enter an address you would like to check the balance of for the **owner** 2. Click **call** ![Get Balance of an Account](/images/builders/ethereum/precompiles/ux/erc20/erc20-7.webp) Your balance will be displayed under the `balanceOf` function. ### Approve a Spend {: #approve-a-spend } To approve a spend, you'll need to provide an address for the spender and the number of tokens that the spender is allowed to spend. The spender can be an externally owned account or a smart contract. For this example, you can approve the spender to spend 1 DEV token. To get started, please follow these steps: 1. Expand the **approve** function 2. Enter the address of the spender. You should have created two accounts before starting, so you can use the second account as the spender 3. Enter the amount of tokens the spender can spend for the **value**. For this example, you can allow the spender to spend 1 DEV token in Wei units (`1000000000000000000`) 4. Click **transact** 5. MetaMask will pop up, and you will be prompted to review the transaction details. Click **View full transaction details** to review the amount to be sent and the address of the spender 6. If everything looks ok, you can click **Confirm** to send the transaction ![Confirm Approve Transaction](/images/builders/ethereum/precompiles/ux/erc20/erc20-8.webp) After the transaction has successfully gone through, you'll notice that the balance of your account hasn't changed. This is because you have only approved the spend for the given amount, and the spender hasn't spent the funds. In the next section, you will use the `allowance` function to verify that the spender is able to spend 1 DEV token on your behalf. ### Get Allowance of Spender {: #get-allowance-of-spender } To check that the spender received the allowance approved in the [Approve a Spend](#approve-a-spend) section, you can: 1. Expand the **allowance** function 2. Enter your address for the **owner** 3. Enter the address of the **spender** that you used in the previous section 4. Click **call** ![Get Allowance of Spender](/images/builders/ethereum/precompiles/ux/erc20/erc20-9.webp) Once the call is complete, the allowance of the spender will be displayed, which should be equivalent to 1 DEV token (`1000000000000000000`). ### Send Transfer {: #send-transfer } To do a standard transfer and send tokens from your account directly to another account, you can call the `transfer` function by following these steps: 1. Expand the **transfer** function 2. Enter the address to send DEV tokens to. You should have created two accounts before starting, so you can use the second account as the recipient 3. Enter the amount of DEV tokens to send. For this example, you can send 1 DEV token (`1000000000000000000`) 4. Click **transact** 5. MetaMask will pop up, you can review the transaction details, and if everything looks good, click **Confirm** ![Send Standard Transfer](/images/builders/ethereum/precompiles/ux/erc20/erc20-10.webp) Once the transaction is complete, you can [check your balance](#get-account-balance) using the `balanceOf` function or by looking at MetaMask, and notice that this time your balance decreased by 1 DEV token. You can also use the `balanceOf` function to ensure that the recipients balance has increased by 1 DEV token as expected. ### Send Transfer From Specific Account {: #send-transferfrom } So far, you should have approved an allowance of 1 DEV token for the spender and sent 1 DEV token via the standard `transfer` function. The `transferFrom` function varies from the standard `transfer` function as it allows you to define the address to which you want to send the tokens. So you can specify an address that has an allowance or your address as long as you have funds. For this example, you will use the spender's account to initiate a transfer of the allowed funds from the owner to the spender. The spender can send the funds to any account, but you can send the funds from the owner to the spender for this example. First, you need to switch to the spender's account in MetaMask. Once you switch to the spender's account, you'll notice that the selected address in Remix under the **Accounts** tab is now the spender's. ![Switch accounts Remix](/images/builders/ethereum/precompiles/ux/erc20/erc20-11.webp) Next, you can initiate and send the transfer, to do so: 1. Expand the **transferFrom** function 2. Enter your address as the owner in the **from** field 3. Enter the recipient address, which should be the spender's address, in the **to** field 4. Enter the amount of DEV tokens to send. Again, the spender is currently only allowed to send 1 DEV token, so enter `1000000000000000000` 5. Click **transact** ![Send Standard Transfer](/images/builders/ethereum/precompiles/ux/erc20/erc20-12.webp) Once the transaction is complete, you can [check the balance](#get-account-balance) of the owner and spender using the `balanceOf` function. The spender's balance should have increased by 1 DEV token, and their allowance should now be depleted. To verify that the spender no longer has an allowance, you can call the `allowance` function, passing in the owner and spender's addresses. You should receive a result of 0. ![Zero Allowance](/images/builders/ethereum/precompiles/ux/erc20/erc20-13.webp) And that's it! You've successfully interacted with the ERC-20 precompile using MetaMask and Remix! --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/interoperability/xcm/xc20/send-xc20s/eth-api/ --- BEGIN CONTENT --- --- title: XCM Precompile description: Learn about the XCM Precompile and how to use it to transfer assets from Moonbeam networks to other parachains. categories: XCM, Precompiles --- # XCM Precompile ## Introduction {: #introduction } As a Polkadot parachain, Moonbeam has the inherent ability to communicate and exchange data with other connected parachains. This native cross-chain communication allows safe and fast token transfers leveraging the Cross-Consensus Message format (XCM for short), facilitating communication between different consensus systems. The communication protocol enabling token transfers is built on [Substrate](/builders/substrate/){target=\_blank} and runs on a lower level than the EVM, making it harder for EVM developers to access. Nevertheless, Moonbeam networks have an XCM Precompile that fills the gap between execution layers. This precompile exposes a smart contract interface that abstracts away the underlying complexities, making the execution of cross-chain token transfers as easy as any other smart contract call. This guide will show you how to interact with the [XCM Interface](https://github.com/Moonsong-Labs/moonkit/blob/main/precompiles/pallet-xcm/XcmInterface.sol){target=\_blank} precompile to execute cross-chain token transfers through the Ethereum API. The XCM Precompile is located at the following address: === "Moonbeam" ```text {{networks.moonbeam.precompiles.xcm_interface }} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.xcm_interface }} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.xcm_interface }} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The XCM Solidity Interface {: #the-xcm-solidity-interface } The [`XCMInterface.sol`](https://github.com/Moonsong-Labs/moonkit/blob/main/precompiles/pallet-xcm/XcmInterface.sol){target=\_blank} is a Solidity interface that allows developers to interact with the methods of `pallet-xcm`. ??? code "XCMInterface.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The XCM contract's address. address constant XCM_CONTRACT_ADDRESS = 0x000000000000000000000000000000000000081A; /// @dev The XCM contract's instance. XCM constant XCM_CONTRACT = XCM(XCM_CONTRACT_ADDRESS); /// @author The Moonbeam Team /// @title XCM precompile Interface /// @dev The interface that Solidity contracts use to interact with the substrate pallet-xcm. interface XCM { // A location is defined by its number of parents and the encoded junctions (interior) struct Location { uint8 parents; bytes[] interior; } // Support for Weights V2 struct Weight { uint64 refTime; uint64 proofSize; } // A way to represent fungible assets in XCM using Location format struct AssetLocationInfo { Location location; uint256 amount; } // A way to represent fungible assets in XCM using address format struct AssetAddressInfo { address asset; uint256 amount; } // The values start at `0` and are represented as `uint8` enum TransferType { Teleport, LocalReserve, DestinationReserve } /// @dev Function to send assets via XCM using transfer_assets() pallet-xcm extrinsic. /// @custom:selector 9ea8ada7 /// @param dest The destination chain. /// @param beneficiary The actual account that will receive the tokens on dest. /// @param assets The combination (array) of assets to send in Location format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsLocation( Location memory dest, Location memory beneficiary, AssetLocationInfo[] memory assets, uint32 feeAssetItem ) external; /// @dev Function to send assets via XCM to a 20 byte-like parachain /// using transfer_assets() pallet-xcm extrinsic. /// @custom:selector a0aeb5fe /// @param paraId The para-id of the destination chain. /// @param beneficiary The actual account that will receive the tokens on paraId destination. /// @param assets The combination (array) of assets to send in Address format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsToPara20( uint32 paraId, address beneficiary, AssetAddressInfo[] memory assets, uint32 feeAssetItem ) external; /// @dev Function to send assets via XCM to a 32 byte-like parachain /// using transfer_assets() pallet-xcm extrinsic. /// @custom:selector f23032c3 /// @param paraId The para-id of the destination chain. /// @param beneficiary The actual account that will receive the tokens on paraId destination. /// @param assets The combination (array) of assets to send in Address format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsToPara32( uint32 paraId, bytes32 beneficiary, AssetAddressInfo[] memory assets, uint32 feeAssetItem ) external; /// @dev Function to send assets via XCM to the relay chain /// using transfer_assets() pallet-xcm extrinsic. /// @custom:selector 6521cc2c /// @param beneficiary The actual account that will receive the tokens on the relay chain. /// @param assets The combination (array) of assets to send in Address format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsToRelay( bytes32 beneficiary, AssetAddressInfo[] memory assets, uint32 feeAssetItem ) external; /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm /// extrinsic. /// Important: in this selector RemoteReserve type (for either assets or fees) is not allowed. /// If users want to send assets and fees (in Location format) with a remote reserve, /// they must use the selector fc19376c. /// @custom:selector 8425d893 /// @param dest The destination chain. /// @param assets The combination (array) of assets to send in Location format. /// @param assetsTransferType The TransferType corresponding to assets being sent. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. /// @param feesTransferType The TransferType corresponding to the asset used as fees. /// @param customXcmOnDest The XCM message to execute on destination chain. function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, TransferType assetsTransferType, uint8 remoteFeesIdIndex, TransferType feesTransferType, bytes memory customXcmOnDest ) external; /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm /// extrinsic. /// @custom:selector fc19376c /// @param dest The destination chain. /// @param assets The combination (array) of assets to send in Location format. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. /// @param customXcmOnDest The XCM message to execute on destination chain. /// @param remoteReserve The remote reserve corresponding for assets and fees. They MUST /// share the same reserve. function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, uint8 remoteFeesIdIndex, bytes memory customXcmOnDest, Location memory remoteReserve ) external; /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm /// extrinsic. /// Important: in this selector RemoteReserve type (for either assets or fees) is not allowed. /// If users want to send assets and fees (in Address format) with a remote reserve, /// they must use the selector aaecfc62. /// @custom:selector 998093ee /// @param dest The destination chain. /// @param assets The combination (array) of assets to send in Address format. /// @param assetsTransferType The TransferType corresponding to assets being sent. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. /// @param feesTransferType The TransferType corresponding to the asset used as fees. /// @param customXcmOnDest The XCM message to execute on destination chain. function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, TransferType assetsTransferType, uint8 remoteFeesIdIndex, TransferType feesTransferType, bytes memory customXcmOnDest ) external; /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm /// extrinsic. /// @custom:selector aaecfc62 /// @param dest The destination chain. /// @param assets The combination (array) of assets to send in Address format. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. /// @param customXcmOnDest The XCM message to execute on destination chain. /// @param remoteReserve The remote reserve corresponding for assets and fees. They MUST /// share the same reserve. function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, uint8 remoteFeesIdIndex, bytes memory customXcmOnDest, Location memory remoteReserve ) external; } ``` The interface includes the necessary data structures along with the following functions: ???+ function "**transferAssetsToPara20**(_paraId, beneficiary, assets, feeAssetItem_) — sends assets via XCM to a 20 byte-like parachain using the underlying `transfer_assets()` transaction included in the XCM pallet module" === "Parameters" - `paraId` *uint32* - the para-id of the destination chain - `beneficiary` *address* - the ECDSA-type account in the destination chain that will receive the tokens - `assets` *AssetAddressInfo[] memory* - an array of assets to send in Address format - `feeAssetItem` *uint32* - the index of the asset that will be used to pay fees === "Example" - `paraId` - 888 - `beneficiary` - 0x3f0Aef9Bd799F1291b80376aD57530D353ab0217 - `assets` - [["0x0000000000000000000000000000000000000802", 1000000000000000000]] - `feeAssetItem` - 0 ??? function "**transferAssetsToPara32**(_paraId, beneficiary, assets, feeAssetItem_) — sends assets via XCM to a 32 byte-like parachain using the underlying `transfer_assets()` transaction included in the XCM pallet module" === "Parameters" - `paraId` *uint32* - the para-id of the destination chain - `beneficiary` *bytes32* - the actual account that will receive the tokens on paraId destination - `assets` *AssetAddressInfo[] memory* - an array of assets to send in Address format - `feeAssetItem` *uint32* - the index of the asset that will be used to pay fees === "Example" - `paraId` - 888 - `beneficiary` - 0xf831d83025f527daeed39a644d64d335a4e627b5f4becc78fb67f05976889a06 - `assets` - [["0x0000000000000000000000000000000000000802", 1000000000000000000]] - `feeAssetItem` - 0 ??? function "**transferAssetsToRelay**(_beneficiary, assets, feeAssetItem_) — sends assets via XCM to the relay chain using the underlying `transfer_assets()` transaction included in the XCM pallet module" === "Parameters" - `beneficiary` *bytes32* - the actual account that will receive the tokens on the relay chain - `assets` *AssetAddressInfo[] memory* - an array of assets to send in Address format - `feeAssetItem` *uint32* - the index of the asset that will be used to pay fees === "Example" - `beneficiary` - 0xf831d83025f527daeed39a644d64d335a4e627b5f4becc78fb67f05976889a06 - `assets` - [["0x0000000000000000000000000000000000000802", 1000000000000000000]] - `feeAssetItem` - 0 ??? function "**transferAssetsLocation**(_dest, beneficiary, assets, feeAssetItem_) — sends assets using the underlying `transfer_assets()` transaction included in the XCM pallet module" === "Parameters" - `dest` *Location memory* - the destination chain - `beneficiary` *Location memory* - the account in the destination chain that will receive the tokens - `assets` *AssetLocationInfo[] memory* - an array of assets to send - `feeAssetItem` *uint32* - the index of the asset that will be used to pay fees === "Example" - `dest` - ["1",[]] - `beneficiary` - [0, ["0x01f831d83025f527daeed39a644d64d335a4e627b5f4becc78fb67f05976889a0600"]] - `assets` - [[[1, ["0x010000000000000000000000000000000000000800"]], 1000000000000000000]] - `feeAssetItem` - 0 ??? function "**transferAssetsUsingTypeAndThenLocation**(_dest, assets, assetsTransferType, remoteFeesIdIndex, feesTransferType, customXcmOnDest_) — sends assets through `transfer_assets_using_type_and_then()` pallet-xcm extrinsic. Important: RemoteReserve type (for either assets or fees) is prohibited. For sending assets and fees (in Location format) with a remote reserve, use the subsequent `transferAssetsUsingTypeAndThenLocation` which shares the same function name as this but takes a different set of parameters" === "Parameters" - `dest` *Location memory* - the destination chain - `assets` *AssetLocationInfo[] memory* - an array of assets to send in Location format - `assetsTransferType` *TransferType* - the TransferType corresponding to assets being sent (Teleport = 0, LocalReserve = 1, DestinationReserve = 2) - `remoteFeesIdIndex` *uint8* - the index of the asset (inside assets array) to use as fees - `feesTransferType` *TransferType* - the TransferType corresponding to the asset used as fees (Teleport = 0, LocalReserve = 1, DestinationReserve = 2) - `customXcmOnDest` *bytes memory* - the XCM message to execute on destination chain === "Example" - `dest` - ["1",[]] - `assets` - [[[1, ["0x010000000000000000000000000000000000000802"]], 1000000000000000000]] - `assetsTransferType` - 0 - `remoteFeesIdIndex` - 0 - `feesTransferType` - 1 - `customXcmOnDest` - 0x0408000400010403001300008a5d784563010d01020400010300f8234bedd9553e7668c4e0d60aced12e22bd2d45 ??? function "**transferAssetsUsingTypeAndThenLocation**(_dest, assets, remoteFeesIdIndex, customXcmOnDest, remoteReserve_) — sends assets through `transfer_assets_using_type_and_then()` pallet-xcm extrinsic. Important: The remote reserve must be shared between assets and fees" === "Parameters" - `dest` *Location memory* - the destination chain - `assets` *AssetLocationInfo[] memory* - an array of assets to send in Location format - `remoteFeesIdIndex` *uint8* - the index of the asset (inside assets array) to use as fees - `customXcmOnDest` *bytes memory* - the XCM message to execute on destination chain - `remoteReserve` *Location memory* - the remote reserve corresponding for assets and fees (must be shared) === "Example" - `dest` - ["1",[]] - `assets` - [[[1, ["0x010000000000000000000000000000000000000800"]], 1000000000000000000]] - `remoteFeesIdIndex` - 0 - `customXcmOnDest` - 0x0408000400010403001300008a5d784563010d01020400010300f8234bedd9553e7668c4e0d60aced12e22bd2d45 - `remoteReserve` - [1,[]] ??? function "**transferAssetsUsingTypeAndThenAddress**(_dest, assets, assetsTransferType, remoteFeesIdIndex, feesTransferType, customXcmOnDest_) — sends assets through `transfer_assets_using_type_and_then()` pallet-xcm extrinsic. Important: RemoteReserve type (for either assets or fees) is not allowed. For sending assets and fees (in Address format) with a remote reserve, use the subsequent `transferAssetsUsingTypeAndThenAddress`, which shares the same name as this function but takes a different set of parameters" === "Parameters" - `dest` *Location memory* - the destination chain - `assets` *AssetAddressInfo[] memory* - an array of assets to send in Address format - `assetsTransferType` *TransferType* - the TransferType corresponding to assets being sent (Teleport = 0, LocalReserve = 1, DestinationReserve = 2) - `remoteFeesIdIndex` *uint8* - the index of the asset (inside assets array) to use as fees - `feesTransferType` *TransferType* - the TransferType corresponding to the asset used as fees (Teleport = 0, LocalReserve = 1, DestinationReserve = 2) - `customXcmOnDest` *bytes memory* - the XCM message to execute on destination chain === "Example" - `dest` - ["1",[]] - `assets` - [["0x0000000000000000000000000000000000000802", 1000000000000000000]] - `assetsTransferType` - 0 - `remoteFeesIdIndex` - 0 - `feesTransferType` - 1 - `customXcmOnDest` - 0x0408000400010403001300008a5d784563010d01020400010300f8234bedd9553e7668c4e0d60aced12e22bd2d45 ??? function "**transferAssetsUsingTypeAndThenAddress**(_dest, assets, remoteFeesIdIndex, customXcmOnDest, remoteReserve_) — sends assets through `transfer_assets_using_type_and_then()` pallet-xcm extrinsic. Important: The remote reserve must be shared between assets and fees" === "Parameters" - `dest` *Location memory* - the destination chain - `assets` *AssetAddressInfo[] memory* - an array of assets to send in Address format - `remoteFeesIdIndex` *uint8* - the index of the asset (inside assets array) to use as fees - `customXcmOnDest` *bytes memory* - the XCM message to execute on destination chain - `remoteReserve` *Location memory* - the remote reserve corresponding for assets and fees (must be shared) === "Example" - `dest` - ["1",[]] - `assets` - [["0x0000000000000000000000000000000000000802", 1000000000000000000]] - `remoteFeesIdIndex` - 0 - `customXcmOnDest` - 0x0408000400010403001300008a5d784563010d01020400010300f8234bedd9553e7668c4e0d60aced12e22bd2d45 - `remoteReserve` - [1,[]] ## Interact with the Solidity Interface {: #interact-with-the-solidity-interface } ### Checking Prerequisites {: #checking-prerequisites } To follow this tutorial, you must have your preferred EVM wallet configured and an account funded with native tokens. You can add Moonbeam to MetaMask wallet following this guide: [Interacting with Moonbeam Using MetaMask](/tokens/connect/metamask/){target=\_blank}. ### Remix Set Up {: #remix-set-up } You can interact with the XCM Precompile using [Remix](https://remix.ethereum.org){target=\_blank}. To add the precompile to Remix, you will need to: 1. Get a copy of [`XCMInterface.sol`](https://github.com/Moonsong-Labs/moonkit/blob/main/precompiles/pallet-xcm/XcmInterface.sol){target=\_blank} 2. Paste the file contents into a Remix file named `XCMInterface.sol` ### Compile the Contract {: #compile-the-contract } Next, you will need to compile the interface in Remix: 1. Click on the **Compile** tab, second from top 2. Compile the interface by clicking on **Compile XcmInterface.sol** ![Compiling XCMInterface.sol](/images/builders/interoperability/xcm/xc20/send-xc20s/eth-api/eth-api-1.webp) When the compilation is completed, you will see a green checkmark next to the **Compile** tab. ### Access the Contract {: #access-the-contract } Instead of deploying the precompile, you will access the interface given the address of the precompiled contract: 1. Click on the **Deploy and Run** tab directly below the **Compile** tab in Remix. Please note that the precompiled contracts are already accessible at their respective addresses. Therefore, there is no deployment step 2. Make sure **Injected Provider - Metamask** is selected in the **ENVIRONMENT** dropdown. Once you select **Injected Provider - Metamask**, you may be prompted by MetaMask to connect your account to Remix if it's not already connected 3. Make sure the correct account is displayed under **ACCOUNT** 4. Ensure **XCM - XcmInterface.sol** is selected in the **CONTRACT** dropdown. Given that it is a precompiled contract, there is no deployment step. Instead, you are going to provide the address of the precompile in the **At Address** field 5. Provide the address of the precompile: `{{networks.moonbeam.precompiles.xcm_interface}}` and click **At Address** ![Access the address](/images/builders/interoperability/xcm/xc20/send-xc20s/eth-api/eth-api-2.webp) The **XCM Interface** precompile will appear in the list of **Deployed Contracts**. ### Send Tokens Over to Another EVM-Compatible Appchain {: #transfer-to-evm-chains } To send tokens over to an account in another EVM-compatible appchain, please follow these steps: 1. Expand the **transferAssetsToPara20** function 2. Enter the appchain ID (paraId) 3. Enter the 20-byte (Ethereum-like) destination account (beneficiary) 4. Specify the tokens to be transferred. Note that this parameter is an array that contains at least one asset. Each asset is specified by its address and the total amount to transfer 5. Enter the index of the asset that will be used to pay the fees. This index is zero-based, so the first element is `0`, the second is `1`, and so on 6. Click **transact** 7. MetaMask will pop up, and you will be prompted to review the transaction details. Click **Confirm** to send the transaction ![Confirm Approve Transaction](/images/builders/interoperability/xcm/xc20/send-xc20s/eth-api/eth-api-3.webp) After the transaction is confirmed, wait a few blocks for the transfer to reach the destination chain and reflect the new balance. ### Send Tokens Over to a Substrate Appchain {: #transfer-to-substrate-chains } To send tokens over to an account in a Substrate appchain, please follow these steps: 1. Expand the **transferAssetsToPara32** function 2. Enter the appchain ID (`paraId`) 3. Enter the sr25519-type destination account (beneficiary) 4. Specify the tokens to be transferred. Note that this parameter is an array that contains at least one asset. Each asset is specified by its address and the total amount to transfer !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. 5. Enter the index of the asset that will be used to pay the fees. This index is zero-based, so the first element is `0`, the second is `1`, and so on 6. Click **transact** 7. MetaMask will pop up, and you will be prompted to review the transaction details. Click **Confirm** to send the transaction ![Confirm Approve Transaction](/images/builders/interoperability/xcm/xc20/send-xc20s/eth-api/eth-api-4.webp) After the transaction is confirmed, wait a few blocks for the transfer to reach the destination chain and reflect the new balance. ### Send Tokens Over to the Relay Chain {: #transfer-to-relay-chain } To send tokens over to an account in the relay chain, please follow these steps: 1. Expand the **transferAssetsToRelay** function 2. Enter the sr25519-type destination account (beneficiary) 3. Specify the tokens to be transferred. Note that this parameter is an array that contains at least one asset. Each asset is specified by its address and the total amount to transfer !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. 4. Enter the index of the asset that will be used to pay the fees. This index is zero-based, so the first element is `0`, the second is `1`, and so on 5. Click **transact** 6. MetaMask will pop up, and you will be prompted to review the transaction details. Click **Confirm** to send the transaction ![Confirm Approve Transaction](/images/builders/interoperability/xcm/xc20/send-xc20s/eth-api/eth-api-5.webp) After the transaction is confirmed, wait a few blocks for the transfer to reach the destination chain and reflect the new balance. ### Send Tokens Over Specific Locations {: #transfer-locations } There are two methods that share names with closely related methods, `transferAssetsUsingTypeAndThenLocation` and `transferAssetsUsingTypeAndThenAddress`. However, these are not duplicates. For each function, there is one that accepts five parameters and another that accepts six. The function with five parameters can only be used when the remote reserve is shared between assets and fees. If the remote reserve is not shared between assets and fees, you can use the six parameter version of the method to specify the information needed. The following example will demonstrate `transferAssetsUsingTypeAndThenAddress` when the remote reverse is shared between assets and fees. To follow along with the tutorial, take the following steps: 1. Expand the **transferAssetsUsingTypeAndThenAddress** function 2. Enter the multilocation that specifies the destination chain. Note that any chain can be specified, regardless of its configuration or type 3. Enter the combination array of assets to send in Address format 4. Enter the index of the asset that will be used to pay the fees. This index is zero-based, so the first element is `0`, the second is `1`, and so on 5. Enter the XCM message to be executed on destination chain. For more information about creating XCM call data see [Send and Execute XCM Messages](/builders/interoperability/xcm/send-execute-xcm/) 6. Enter the remote reserve, e.g. `[1,[]]` 7. Click **transact** 8. MetaMask will pop up, and you will be prompted to review the transaction details. Click **Confirm** to send the transaction ![Confirm Approve Transaction](/images/builders/interoperability/xcm/xc20/send-xc20s/eth-api/eth-api-6.webp) After the transaction is confirmed, wait a few blocks for the transfer to reach the destination chain and reflect the new balance. --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/builders/interoperability/xcm/xc20/send-xc20s/xtokens-precompile/ --- BEGIN CONTENT --- --- title: Send XC-20s to Other Chains description: Learn how to send assets cross-chain via Cross-Consensus Messaging (XCM) using the X-Tokens Precompile with familiar Ethereum libraries like Ethers and Web3. categories: Precompiles, XC-20, Ethereum Toolkit --- # Using the X-Tokens Precompile To Send XC-20s ## Introduction {: #introduction } Building an XCM message for fungible asset transfers is not an easy task. Consequently, there are wrapper functions and pallets that developers can leverage to use XCM features on Polkadot and Kusama. One example of such wrappers is the [Polkadot XCM](/builders/interoperability/xcm/xc20/send-xc20s/xcm-pallet/){target=\_blank} Pallet, which provides different methods to transfer fungible assets via XCM. The [Polkadot XCM Pallet](/builders/interoperability/xcm/xc20/send-xc20s/xcm-pallet/){target=\_blank} is coded in Rust and is normally not accessible from the Ethereum API side of Moonbeam. However, the [XCM Precompile](/builders/interoperability/xcm/xc20/send-xc20s/eth-api/){target=\_blank} and the X-Tokens Precompile allow you to interact directly with the Polkadot XCM pallet to send XC-20s from a Solidity interface. This guide will show you how to leverage the X-Tokens Precompile to send [XC-20s](/builders/interoperability/xcm/xc20/overview/){target=\_blank} from a Moonbeam-based network to other chains in the ecosystem (relay chain/parachains) using Ethereum libraries like Ethers and Web3. **Developers must understand that sending incorrect XCM messages can result in the loss of funds.** Consequently, it is essential to test XCM features on a TestNet before moving to a production environment. ## X-Tokens Precompile Contract Address {: #contract-address } The X-Tokens Precompile is located at the following addresses: === "Moonbeam" ```text {{networks.moonbeam.precompiles.xtokens}} ``` === "Moonriver" ```text {{networks.moonriver.precompiles.xtokens}} ``` === "Moonbase Alpha" ```text {{networks.moonbase.precompiles.xtokens}} ``` !!! note There can be some unintended consequences when using the precompiled contracts on Moonbeam. Please refer to the [Security Considerations](/learn/core-concepts/security/){target=\_blank} page for more information. ## The X-Tokens Solidity Interface {: #xtokens-solidity-interface } [Xtokens.sol](https://github.com/moonbeam-foundation/moonbeam/blob/master/precompiles/xtokens/Xtokens.sol){target=\_blank} is an interface through which developers can interact with the X-Tokens Pallet using the Ethereum API. ??? code "Xtokens.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The Xtokens contract's address. address constant XTOKENS_ADDRESS = 0x0000000000000000000000000000000000000804; /// @dev The Xtokens contract's instance. Xtokens constant XTOKENS_CONTRACT = Xtokens(XTOKENS_ADDRESS); /// @author The Moonbeam Team /// @title Xtokens Interface /// @dev The interface through which solidity contracts will interact with xtokens pallet /// @custom:address 0x0000000000000000000000000000000000000804 interface Xtokens { // A multilocation is defined by its number of parents and the encoded junctions (interior) struct Multilocation { uint8 parents; bytes[] interior; } // A MultiAsset is defined by a multilocation and an amount struct MultiAsset { Multilocation location; uint256 amount; } // A Currency is defined by address and the amount to be transferred struct Currency { address currencyAddress; uint256 amount; } /// Transfer a token through XCM based on its currencyId /// /// @dev The token transfer burns/transfers the corresponding amount before sending /// @param currencyAddress The ERC20 address of the currency we want to transfer /// @param amount The amount of tokens we want to transfer /// @param destination The Multilocation to which we want to send the tokens /// @param weight The weight we want to buy in the destination chain /// (uint64::MAX means Unlimited weight) /// @custom:selector b9f813ff function transfer( address currencyAddress, uint256 amount, Multilocation memory destination, uint64 weight ) external; /// Transfer a token through XCM based on its currencyId specifying fee /// /// @dev The token transfer burns/transfers the corresponding amount before sending /// @param currencyAddress The ERC20 address of the currency we want to transfer /// @param amount The amount of tokens we want to transfer /// @param destination The Multilocation to which we want to send the tokens /// @param weight The weight we want to buy in the destination chain /// (uint64::MAX means Unlimited weight) /// @custom:selector 3e506ef0 function transferWithFee( address currencyAddress, uint256 amount, uint256 fee, Multilocation memory destination, uint64 weight ) external; /// Transfer a token through XCM based on its MultiLocation /// /// @dev The token transfer burns/transfers the corresponding amount before sending /// @param asset The asset we want to transfer, defined by its multilocation. /// Currently only Concrete Fungible assets /// @param amount The amount of tokens we want to transfer /// @param destination The Multilocation to which we want to send the tokens /// @param weight The weight we want to buy in the destination chain /// (uint64::MAX means Unlimited weight) /// @custom:selector b4f76f96 function transferMultiasset( Multilocation memory asset, uint256 amount, Multilocation memory destination, uint64 weight ) external; /// Transfer a token through XCM based on its MultiLocation specifying fee /// /// @dev The token transfer burns/transfers the corresponding amount before sending /// @param asset The asset we want to transfer, defined by its multilocation. /// Currently only Concrete Fungible assets /// @param amount The amount of tokens we want to transfer /// @param destination The Multilocation to which we want to send the tokens /// @param weight The weight we want to buy in the destination chain /// (uint64::MAX means Unlimited weight) /// @custom:selector 150c016a function transferMultiassetWithFee( Multilocation memory asset, uint256 amount, uint256 fee, Multilocation memory destination, uint64 weight ) external; /// Transfer several tokens at once through XCM based on its address specifying fee /// /// @dev The token transfer burns/transfers the corresponding amount before sending /// @param currencies The currencies we want to transfer, defined by their address and amount. /// @param feeItem Which of the currencies to be used as fee /// @param destination The Multilocation to which we want to send the tokens /// @param weight The weight we want to buy in the destination chain /// (uint64::MAX means Unlimited weight) /// @custom:selector ab946323 function transferMultiCurrencies( Currency[] memory currencies, uint32 feeItem, Multilocation memory destination, uint64 weight ) external; /// Transfer several tokens at once through XCM based on its location specifying fee /// /// @dev The token transfer burns/transfers the corresponding amount before sending /// @param assets The assets we want to transfer, defined by their location and amount. /// @param feeItem Which of the currencies to be used as fee /// @param destination The Multilocation to which we want to send the tokens /// @param weight The weight we want to buy in the destination chain /// (uint64::MAX means Unlimited weight) /// @custom:selector 797b45fd function transferMultiAssets( MultiAsset[] memory assets, uint32 feeItem, Multilocation memory destination, uint64 weight ) external; } ``` The interface includes the following functions: ???+ function "**transfer**(*address* currencyAddress, *uint256* amount, *Multilocation* *memory* destination, *uint64* weight) — transfer a currency, given the contract address of the currency" === "Parameters" - `currencyAddress` - the address of the asset to transfer - For [External XC-20s](/builders/interoperability/xcm/xc20/overview/#external-xc20s){target=\_blank}, provide the [XC-20 precompile address](/builders/interoperability/xcm/xc20/overview/#current-xc20-assets){target=\_blank} - For native tokens (i.e., GLMR, MOVR, and DEV), provide the [ERC-20 precompile](/builders/ethereum/precompiles/ux/erc20/#the-erc20-interface){target=\_blank} address, which is `{{networks.moonbeam.precompiles.erc20 }}` - For [Local XC-20s](/builders/interoperability/xcm/xc20/overview/#local-xc20s){target=\_blank}, provide the token's address - `amount` - the number of tokens that are going to be sent via XCM - `destination` - the multilocation of the destination address for the tokens being sent via XCM. It supports different address formats, such as 20- or 32-byte addresses (Ethereum or Substrate). The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `weight` - the weight to be purchased to pay for XCM execution on the destination chain, which is charged from the transferred asset ??? function "**transferWithFee**(*address* currencyAddress, *uint256* amount, *uint256* fee, *Multilocation* *memory* destination, *uint64* weight) — transfer a currency, defined as either the native token (self-reserved) or the asset ID, and specify the fee separately from the amount" === "Parameters" - `currencyAddress` - the address of the asset to transfer - For [External XC-20s](/builders/interoperability/xcm/xc20/overview/#external-xc20s){target=\_blank}, provide the [XC-20 precompile address](/builders/interoperability/xcm/xc20/overview/#current-xc20-assets){target=\_blank} - For native tokens (i.e., GLMR, MOVR, and DEV), provide the [ERC-20 precompile](/builders/ethereum/precompiles/ux/erc20/#the-erc20-interface){target=\_blank} address, which is `{{networks.moonbeam.precompiles.erc20 }}` - For [Local XC-20s](/builders/interoperability/xcm/xc20/overview/#local-xc20s){target=\_blank}, provide the token's address - `amount` - the number of tokens that are going to be sent via XCM - `fee` — the amount to be spent to pay for the XCM execution in the target (destination) chain. If this value is not high enough to cover execution costs, the assets will be trapped in the destination chain - `destination` - the multilocation of the destination address for the tokens being sent via XCM. It supports different address formats, such as 20- or 32-byte addresses (Ethereum or Substrate). The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `weight` - the weight to be purchased to pay for XCM execution on the destination chain, which is charged from the transferred asset ??? function "**transferMultiasset**(*Multilocation* *memory* asset, *uint256* amount, *Multilocation* *memory* destination, *uint64* weight) — transfer a fungible asset, defined by its multilocation" === "Parameters" - `asset` - the multilocation of the asset to transfer. The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `amount` - the number of tokens that are going to be sent via XCM - `destination` - the multilocation of the destination address for the tokens being sent via XCM. It supports different address formats, such as 20- or 32-byte addresses (Ethereum or Substrate). The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `weight` - the weight to be purchased to pay for XCM execution on the destination chain, which is charged from the transferred asset ??? function "**transferMultiassetWithFee**(*Multilocation* *memory* asset, *uint256* amount, *uint256* fee, *Multilocation* *memory* destination, *uint64* weight) — transfer a fungible asset, defined by its multilocation, and pay the fee with a different asset, also defined by its multilocation" === "Parameters" - `asset` - the multilocation of the asset to transfer. The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `amount` - the number of tokens that are going to be sent via XCM - `fee` — the amount to be spent to pay for the XCM execution in the target (destination) chain. If this value is not high enough to cover execution costs, the assets will be trapped in the destination chain - `destination` - the multilocation of the destination address for the tokens being sent via XCM. It supports different address formats, such as 20- or 32-byte addresses (Ethereum or Substrate). The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `weight` - the weight to be purchased to pay for XCM execution on the destination chain, which is charged from the transferred asset ??? function "**transferMulticurrencies**(*Currency[]* *memory* currencies, *uint32* feeItem, *Multilocation* *memory* destination, *uint64* weight) — transfer different currencies, specifying which is used as the fee. Each currency is defined as either the native token (self-reserved) or the asset ID" === "Parameters" - `currencies` - an array of the currencies to send, which are identified by their currency address, and the amount to send - `feeItem` — an index to define the asset position of an array of assets being sent, used to pay for the XCM execution in the target chain. For example, if only one asset is being sent, the `feeItem` would be `0` - `destination` - the multilocation of the destination address for the tokens being sent via XCM. It supports different address formats, such as 20- or 32-byte addresses (Ethereum or Substrate). The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `weight` - the weight to be purchased to pay for XCM execution on the destination chain, which is charged from the transferred asset ??? function "**transferMultiassets**(*MultiAsset[]* *memory* assets, *uint32* feeItem, *Multilocation* *memory* destination, *uint64* weight) — transfer several fungible assets, defined by their multilocation, and pay the fee with one of the assets, also defined by its multilocation" === "Parameters" - `assets` - an array of the multilocations of each asset to transfer. The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `feeItem` — an index to define the asset position of an array of assets being sent, used to pay for the XCM execution in the target chain. For example, if only one asset is being sent, the `feeItem` would be `0` - `destination` - the multilocation of the destination address for the tokens being sent via XCM. It supports different address formats, such as 20- or 32-byte addresses (Ethereum or Substrate). The multilocation must be formatted in a particular way, which is described in the [Building the Precompile Multilocation](#building-the-precompile-multilocation) section - `weight` - the weight to be purchased to pay for XCM execution on the destination chain, which is charged from the transferred asset ## Building the Precompile Multilocation {: #building-the-precompile-multilocation } [Multilocations](/builders/interoperability/xcm/core-concepts/multilocations/){target=\_blank} define a specific point in the entire relay chain/parachain ecosystem relative to a given origin. They are frequently used by the X-Tokens Precompile to define the location of assets and destination chains and accounts. Multilocations need to be formatted in a specific way that precompiles can understand, which is different than the format seen when interacting with pallets. In the X-Tokens Precompile interface, the `Multilocation` structure is defined as follows: ```solidity struct Multilocation { uint8 parents; bytes[] interior; } ``` As with a standard [multilocation](/builders/interoperability/xcm/core-concepts/multilocations/){target=\_blank}, there are `parents` and `interior` elements. However, instead of defining the multilocation as an object, with Ethereum libraries, the struct is defined as an array, which contains a `uint8` for the `parents` as the first element and a bytes array for the `interior` as the second element. The normal values you would see for the `parents` element are: | Origin | Destination | Parents Value | |:-----------:|:-----------:|:-------------:| | Parachain A | Parachain A | 0 | | Parachain A | Relay Chain | 1 | | Parachain A | Parachain B | 1 | For the `interior` element, the number of fields you need to drill down to in the target chain to reach the exact location of the target, such as the specific asset or account, represents the size of the bytes array: | Array | Size | Interior Value | |:------------:|:----:|:--------------:| | [] | 0 | Here | | [XYZ] | 1 | X1 | | [XYZ, ABC] | 2 | X2 | | [XYZ, ... N] | N | XN | !!! note Interior value `Here` is often used for the relay chain (either as a destination or to target the relay chain asset). Each field required to reach the exact location of the target needs to be defined as a hex string. The first byte (2 hexadecimal characters) corresponds to the selector of the field. For example: | Byte Value | Selector | Data Type | |:----------:|:--------------:|-----------| | 0x00 | Parachain | bytes4 | | 0x01 | AccountId32 | bytes32 | | 0x02 | AccountIndex64 | u64 | | 0x03 | AccountKey20 | bytes20 | | 0x04 | PalletInstance | byte | | 0x05 | GeneralIndex | u128 | | 0x06 | GeneralKey | bytes[] | Next, depending on the selector and its data type, the following bytes correspond to the actual data being provided. Note that for `AccountId32`, `AccountIndex64`, and `AccountKey20`, the optional `network` field is appended at the end. For example: | Selector | Data Value | Represents | |:--------------:|:----------------------:|:----------------------------------:| | Parachain | "0x00+000007E7" | Parachain ID 2023 | | AccountId32 | "0x01+AccountId32+00" | AccountId32, Network(Option) Null | | AccountId32 | "0x01+AccountId32+03" | AccountId32, Network Polkadot | | AccountKey20 | "0x03+AccountKey20+00" | AccountKey20, Network(Option) Null | | PalletInstance | "0x04+03" | Pallet Instance 3 | !!! note The `interior` data usually needs to be wrapped around quotes, or you might get an `invalid tuple value` error. The following code snippet goes through some examples of multilocation structures, as they would need to be fed into the X-Tokens Precompile functions: ```js // Multilocation targeting the relay chain or its asset from a parachain [ 1, // parents = 1 [], // interior = here ] // Multilocation targeting Moonbase Alpha DEV token from another parachain [ 1, // parents = 1 [ // interior = X2 (the array has a length of 2) '0x00000003E8', // Parachain selector + Parachain ID 1000 (Moonbase Alpha) '0x0403', // Pallet Instance selector + Pallet Instance 3 (Balances Pallet) ], ] // Multilocation targeting Alice's account on the relay chain from Moonbase Alpha [ 1, // parents = 1 [ // interior = X1 (the array has a length of 1) // AccountKey32 selector + AccountId32 address in hex + Network(Option) Null '0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300', ], ] ``` ## Building an XCM Message {: #build-xcm-xtokens-precompile } This guide covers the process of building an XCM message using the X-Tokens Precompile, more specifically, with the `transfer` and `transferMultiasset` functions. Nevertheless, these two cases can be extrapolated to the other functions of the precompile, especially once you become familiar with multilocations. You'll be transferring xcUNIT tokens, which are the [XC-20](/builders/interoperability/xcm/xc20/overview/){target=\_blank} representation of the Alphanet relay chain token, UNIT. You can adapt this guide for any other XC-20. ### Checking Prerequisites {: #xtokens-check-prerequisites} To follow along with the examples in this guide, you need to have the following: - The ABI of the X-Tokens Precompile ??? code "X-Tokens Precompile ABI" ```js export default [ { inputs: [ { internalType: 'address', name: 'currencyAddress', type: 'address', }, { internalType: 'uint256', name: 'amount', type: 'uint256', }, { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'destination', type: 'tuple', }, { internalType: 'uint64', name: 'weight', type: 'uint64', }, ], name: 'transfer', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'location', type: 'tuple', }, { internalType: 'uint256', name: 'amount', type: 'uint256', }, ], internalType: 'struct Xtokens.MultiAsset[]', name: 'assets', type: 'tuple[]', }, { internalType: 'uint32', name: 'feeItem', type: 'uint32', }, { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'destination', type: 'tuple', }, { internalType: 'uint64', name: 'weight', type: 'uint64', }, ], name: 'transferMultiAssets', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'address', name: 'currencyAddress', type: 'address', }, { internalType: 'uint256', name: 'amount', type: 'uint256', }, ], internalType: 'struct Xtokens.Currency[]', name: 'currencies', type: 'tuple[]', }, { internalType: 'uint32', name: 'feeItem', type: 'uint32', }, { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'destination', type: 'tuple', }, { internalType: 'uint64', name: 'weight', type: 'uint64', }, ], name: 'transferMultiCurrencies', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'asset', type: 'tuple', }, { internalType: 'uint256', name: 'amount', type: 'uint256', }, { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'destination', type: 'tuple', }, { internalType: 'uint64', name: 'weight', type: 'uint64', }, ], name: 'transferMultiasset', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'asset', type: 'tuple', }, { internalType: 'uint256', name: 'amount', type: 'uint256', }, { internalType: 'uint256', name: 'fee', type: 'uint256', }, { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'destination', type: 'tuple', }, { internalType: 'uint64', name: 'weight', type: 'uint64', }, ], name: 'transferMultiassetWithFee', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'currencyAddress', type: 'address', }, { internalType: 'uint256', name: 'amount', type: 'uint256', }, { internalType: 'uint256', name: 'fee', type: 'uint256', }, { components: [ { internalType: 'uint8', name: 'parents', type: 'uint8', }, { internalType: 'bytes[]', name: 'interior', type: 'bytes[]', }, ], internalType: 'struct Xtokens.Multilocation', name: 'destination', type: 'tuple', }, { internalType: 'uint64', name: 'weight', type: 'uint64', }, ], name: 'transferWithFee', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ]; ``` - An account with funds. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} - Some xcUNIT tokens. You can swap DEV tokens (Moonbase Alpha's native token) for xcUNITs on [Moonbeam-Swap](https://moonbeam-swap.netlify.app/#/swap){target=\_blank}, a demo Uniswap-V2 clone on Moonbase Alpha !!! note You can adapt this guide to transfer another [external XC-20 or a local XC-20](/builders/interoperability/xcm/xc20/overview/){target=\_blank}. For external XC-20s, you'll need the asset ID and the number of decimals the asset has. For local XC-20s, you'll need the contract address. ![Moonbeam Swap xcUNIT](/images/builders/interoperability/xcm/xc20/send-xc20s/xcm-pallet/xtokens-1.webp) To check your xcUNIT balance, you can add the XC-20's [precompile address](/builders/interoperability/xcm/xc20/interact/#calculate-xc20-address){target=\_blank} to MetaMask with the following address: ```text 0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080 ``` !!! note To test out the examples on Moonbeam or Moonriver, you can replace the RPC URL with your own endpoint and API key, which you can get from one of the supported [Endpoint Providers](/builders/get-started/endpoints/){target=\_blank}. ### Determining Weight Needed for XCM Execution {: #determining-weight } To determine the weight needed for XCM execution on the destination chain, you'll need to know which XCM instructions are executed on the destination chain. You can find an overview of the XCM instructions used in the [XCM Instructions for Transfers via X-Tokens](/builders/interoperability/xcm/xc20/send-xc20s/overview/#xcm-instructions-for-asset-transfers){target=\_blank} guide. !!! note Some weights include database reads and writes; for example, the `WithdrawAsset` and `DepositAsset` instructions include both one database read and one write. To get the total weight, you'll need to add the weight of any required database reads or writes to the base weight of the given instruction. For Westend-based relay chains, like Alphanet, you can get the weight cost for read and write database operations for [Rocks DB](https://github.com/paritytech/polkadot-sdk/blob/polkadot-{{ networks.alphanet.spec_version }}/polkadot/runtime/westend/constants/src/weights/rocksdb_weights.rs#L27-L31){target=\_blank} (which is the default database) in the [polkadot-sdk](https://github.com/paritytech/polkadot-sdk){target=\_blank} repository on GitHub. Since Alphanet is a Westend-based relay chain, you can refer to the instruction weights defined in the [Westend runtime code](https://github.com/paritytech/polkadot-sdk/tree/polkadot-{{ networks.alphanet.spec_version }}/polkadot/runtime/westend){target=\_blank}, which are broken up into two types of instructions: [fungible](https://github.com/paritytech/polkadot-sdk/blob/polkadot-{{ networks.alphanet.spec_version }}/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs){target=\_blank} and [generic](https://github.com/paritytech/polkadot-sdk/blob/polkadot-{{ networks.alphanet.spec_version }}/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs){target=\_blank}. It's important to note that each chain defines its own weight requirements. To determine the weight required for each XCM instruction on a given chain, please refer to the chain's documentation or reach out to a member of their team. To learn how to find the weights required by Moonbeam, Polkadot, or Kusama, you can refer to our documentation on [Weights and Fees](/builders/interoperability/xcm/core-concepts/weights-fees/){target=\_blank}. ### X-Tokens Precompile Transfer Function {: #precompile-transfer } To use the `transfer` function of the X-Tokens Precompile, you'll take these general steps: 1. Create a provider using a Moonbase Alpha RPC endpoint 2. Create a signer to send the transaction. This example uses a private key to create the signer and is for demo purposes only. **Never store your private key in a JavaScript file** 3. Create a contract instance of the X-Tokens Precompile using the address and ABI of the precompile 4. Assemble the arguments for the `transfer` function: - `currencyAddress` - the address for xcUNIT: `0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080` - `amount` - 1 xcUNIT. Since xcUNIT has 12 decimals, you can use: `1000000000000` - `destination` - the multilocation of the destination, which targets Alice's account on the relay chain: `'0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'` - `weight` - the [weight](#determining-weight) to purchase for the XCM execution on the destination chain: `{{ networks.alphanet.xcm_message.transfer.weight.display }}` 5. Create the `transfer` function, passing in the arguments 6. Sign and send the transaction === "Ethers.js" ```js import { ethers } from 'ethers'; // Import Ethers library import abi from './xtokensABI.js'; // Import the X-Tokens ABI const privateKey = 'INSERT_PRIVATE_KEY'; // Create Ethers provider and signer const provider = new ethers.JsonRpcProvider( 'https://rpc.api.moonbase.moonbeam.network' ); const signer = new ethers.Wallet(privateKey, provider); // Create X-Tokens contract instance const xTokens = new ethers.Contract( '0x0000000000000000000000000000000000000804', abi, signer ); // Arguments for the transfer function const currencyAddress = '0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080'; // xcUNIT address const amount = 1000000000000; const destination = [ // Target the relay chain from Moonbase Alpha 1, // Target Alice's 32-byte relay chain account ['0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'], ]; const weight = 305986000; // Sends 1 xcUNIT to the relay chain using the transfer function async function transferToAlice() { // Creates, signs, and sends the transfer transaction const transaction = await xTokens.transfer( currencyAddress, amount, destination, weight ); // Waits for the transaction to be included in a block await transaction.wait(); console.log(transaction); } transferToAlice(); ``` === "Web3.js" ```js import Web3 from 'web3'; // Import Web3 library import abi from './xtokensABI.js'; // Import the X-Tokens ABI const privateKey = 'INSERT_PRIVATE_KEY'; // Create Web3 provider const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network'); // Change to network of choice // Create contract instance const xTokens = new web3.eth.Contract( abi, '0x0000000000000000000000000000000000000804', { from: web3.eth.accounts.privateKeyToAccount(privateKey).address } // 'from' is necessary for gas estimation ); // Arguments for the transfer function const currencyAddress = '0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080'; // xcUNIT address const amount = 1000000000000; const destination = [ // Target the relay chain from Moonbase Alpha 1, // Target Alice's 32-byte relay chain account ['0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'], ]; const weight = 305986000; // Sends 1 xcUNIT to the relay chain using the transfer function async function transferToAlice() { // Create transaction const transferTx = xTokens.methods.transfer( currencyAddress, amount, destination, weight ); // Sign transaction const signedTx = await web3.eth.accounts.signTransaction( { to: '0x0000000000000000000000000000000000000804', data: transferTx.encodeABI(), gas: await transferTx.estimateGas(), }, privateKey ); // Send signed transaction const sendTx = await web3.eth.sendSignedTransaction(signedTx.rawTransaction); console.log(sendTx); } transferToAlice(); ``` === "Web3.py" ```py from web3 import Web3 abi = "INSERT_XTOKENS_ABI" # Paste or import the x-tokens ABI private_key = "INSERT_PRIVATE_KEY" # This is for demo purposes, never store your private key in plain text address = "INSERT_ADDRESS" # The wallet address that corresponds to your private key # Create Web3 provider web3 = Web3(Web3.HTTPProvider("https://rpc.api.moonbase.moonbeam.network")) # Create contract instance x_tokens = web3.eth.contract( address="0x0000000000000000000000000000000000000804", abi=abi ) # Arguments for the transfer function currencyAddress = "0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080" # xcUNIT address amount = 1000000000000 destination = [ # Target the relay chain from Moonbase Alpha 1, # Target Alice's 32-byte relay chain account ["0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300"], ] weight = 305986000 # Sends 1 xcUNIT to the relay chain using the transfer function def transfer_to_alice(): # Create transaction transferTx = x_tokens.functions.transfer( currencyAddress, amount, destination, weight ).build_transaction( { "from": address, "nonce": web3.eth.get_transaction_count(address), } ) # Sign transaction signedTx = web3.eth.account.sign_transaction(transferTx, private_key) # Send tx and wait for receipt hash = web3.eth.send_raw_transaction(signedTx.rawTransaction) receipt = web3.eth.wait_for_transaction_receipt(hash) print(f"Tx successful with hash: { receipt.transactionHash.hex() }") transfer_to_alice() ``` ### X-Tokens Precompile Transfer Multiasset Function {: #precompile-transfer-multiasset} To use the `transfer` function of the X-Tokens Precompile, you'll take these general steps: 1. Create a provider using a Moonbase Alpha RPC endpoint 2. Create a signer to send the transaction. This example uses a private key to create the signer and is for demo purposes only. **Never store your private key in a JavaScript file** 3. Create a contract instance of the X-Tokens Precompile using the address and ABI of the precompile 4. Assemble the arguments for the `transferMultiasset` function: - `asset` - the multilocation for xcUNIT: `[1, []]` - `amount` - 1 xcUNIT. Since xcUNIT has 12 decimals, you can use: `1000000000000` - `destination` - the multilocation of the destination, which targets Alice's account on the relay chain: `'0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'` - `weight` - the [weight](#determining-weight) to purchase for the XCM execution on the destination chain: `{{ networks.alphanet.xcm_message.transfer.weight.numbers_only }}` 5. Create the `transferMultiasset` function, passing in the arguments 6. Sign and send the transaction === "Ethers.js" ```js import { ethers } from 'ethers'; // Import Ethers library import abi from './xtokensABI.js'; // Import the X-Tokens ABI const privateKey = 'INSERT_PRIVATE_KEY'; // Create Ethers provider and signer const provider = new ethers.JsonRpcProvider( 'https://rpc.api.moonbase.moonbeam.network' ); const signer = new ethers.Wallet(privateKey, provider); // Create X-Tokens contract instance const xTokens = new ethers.Contract( '0x0000000000000000000000000000000000000804', abi, signer ); // Arguments for the transfer multiasset function const asset = [1, []]; // Multilocation targeting the relay chain const amount = 1000000000000; const dest = [ // Target the relay chain from Moonbase Alpha 1, // Target Alice's 32-byte relay chain account ['0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'], ]; const weight = 305986000; // Sends 1 xcUNIT to the relay chain using the transferMultiasset function async function transferMultiassetToAlice() { const transaction = await xTokens.transferMultiasset( asset, amount, dest, weight ); await transaction.wait(); console.log(transaction); } transferMultiassetToAlice(); ``` === "Web3.js" ```js import Web3 from 'web3'; // Import Web3 library import abi from './xtokensABI.js'; // Import the X-Tokens ABI const privateKey = 'INSERT_PRIVATE_KEY'; // Create Web3 provider const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network'); // Change to network of choice // Create contract instance const xTokens = new web3.eth.Contract( abi, '0x0000000000000000000000000000000000000804', { from: web3.eth.accounts.privateKeyToAccount(privateKey).address } // 'from' is necessary for gas estimation ); // Arguments for the transfer multiasset function const asset = [1, []]; // Multilocation targeting the relay chain const amount = 1000000000000; const dest = [ // Target the relay chain from Moonbase Alpha 1, // Target Alice's 32-byte relay chain account ['0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'], ]; const weight = 305986000; // Sends 1 xcUNIT to the relay chain using the transferMultiasset function async function transferMultiassetToAlice() { // Create transaction const transferTx = xTokens.methods.transferMultiasset( asset, amount, dest, weight ); // Sign transaction const signedTx = await web3.eth.accounts.signTransaction( { to: '0x0000000000000000000000000000000000000804', data: transferTx.encodeABI(), gas: await transferTx.estimateGas(), }, privateKey ); // Send signed transaction const sendTx = await web3.eth.sendSignedTransaction(signedTx.rawTransaction); console.log(sendTx); } transferMultiassetToAlice(); ``` === "Web3.py" ```py from web3 import Web3 abi = "INSERT_XTOKENS_ABI" # Paste or import the x-tokens ABI private_key = "INSERT_PRIVATE_KEY" # This is for demo purposes, never store your private key in plain text address = "INSERT_ADDRESS" # The wallet address that corresponds to your private key # Create Web3 provider web3 = Web3(Web3.HTTPProvider("https://rpc.api.moonbase.moonbeam.network")) # Create contract instance x_tokens = web3.eth.contract( address="0x0000000000000000000000000000000000000804", abi=abi ) # Arguments for the transfer function asset = [1, []] # Multilocation targeting the relay chain amount = 1000000000000 dest = [ # Target the relay chain from Moonbase Alpha 1, # Target Alice's 32-byte relay chain account ["0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300"], ] weight = 305986000 # Sends 1 xcUNIT to the relay chain using the transferMultiasset function def transfer_multiasset_to_alice(): # Create transaction transferTx = x_tokens.functions.transferMultiasset( asset, amount, dest, weight ).build_transaction( { "from": address, "nonce": web3.eth.get_transaction_count(address), } ) # Sign transaction signedTx = web3.eth.account.sign_transaction(transferTx, private_key) # Send tx and wait for receipt hash = web3.eth.send_raw_transaction(signedTx.rawTransaction) receipt = web3.eth.wait_for_transaction_receipt(hash) print(f"Tx successful with hash: { receipt.transactionHash.hex() }") transfer_multiasset_to_alice() ``` --- END CONTENT --- Doc-Content: https://docs.moonbeam.network/tutorials/eth-api/batch-approve-swap/ --- BEGIN CONTENT --- --- title: Approve & Swap with the Batch Precompile description: Learn how to use the Batch Precompile on Moonbeam to batch an approval and swap into a single call, so you can approve the exact amount of tokens for the swap. categories: Tutorials, Precompiles --- # Use the Batch Precompile to Approve and Swap Tokens in a Single Transaction _by Erin Shaben_ ## Introduction {: #introduction } Token approvals are critical for interacting with smart contracts securely, preventing smart contracts without permission from accessing a user's tokens. When a smart contract is given approval to access a user's tokens, the amount of tokens it has access to is often an unlimited amount, depending on the DApp. One of the reasons why many DApps use an unlimited amount is so that users don't need to continue to sign approval transactions every time they want to move their tokens. This is in addition to the second transaction required to actually swap the tokens. For networks like Ethereum, this can be expensive. However, if the approved smart contract has a vulnerability, it could be exploited and the users' tokens could be transferred at any time without requiring further approval. In addition, if a user no longer wants the DApp's contract to have access to their tokens, they have to revoke the token approval, which requires another transaction to be sent. As a DApp developer on Moonbeam, you can avoid this process entirely, providing users with more control over their assets. This can be done using the [batch precompile](/builders/ethereum/precompiles/ux/batch/){target=\_blank} to batch an approval and swap into a single transaction, instead of the typical two transaction process. This allows for the approval amount to be the exact swap amount instead of having unlimited access to your users' tokens. In this tutorial, we'll dive into the process of batching an approval and swap into one transaction using the `batchAll` function of the batch precompile contract. We'll create and deploy an ERC-20 contract and a simple DEX contract for the swap on the [Moonbase Alpha TestNet](/builders/get-started/networks/moonbase/){target=\_blank} using [Hardhat](/builders/ethereum/dev-env/hardhat/){target=\_blank} and [Ethers](/builders/ethereum/libraries/ethersjs/){target=\_blank}. ## Checking Prerequisites {: #checking-prerequisites } For this tutorial, you'll need the following: - An account with funds. You can get DEV tokens for testing on Moonbase Alpha once every 24 hours from the [Moonbase Alpha Faucet](https://faucet.moonbeam.network){target=\_blank} - An empty Hardhat project that is configured for the Moonbase Alpha TestNet. For step-by-step instructions, please refer to the [Creating a Hardhat Project](/builders/ethereum/dev-env/hardhat/#creating-a-hardhat-project){target=\_blank} and the [Hardhat Configuration File](/builders/ethereum/dev-env/hardhat/#hardhat-configuration-file){target=\_blank} sections of our Hardhat documentation page - To test out the examples in this guide on Moonbeam or Moonriver, you will need to have your own endpoint and API key, which you can get from one of the supported [Endpoint Providers](/builders/get-started/endpoints/){target=\_blank} ### Install Dependencies {: #install-dependencies } Once you have your [Hardhat project](/builders/ethereum/dev-env/hardhat/){target=\_blank}, you can install the [Ethers plugin](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-ethers){target=\_blank}. This provides a convenient way to use the [Ethers.js](/builders/ethereum/libraries/ethersjs/){target=\_blank} library to interact with the network. You can also install the [OpenZeppelin contracts library](https://docs.openzeppelin.com/contracts){target=\_blank}, as we'll be importing the `ERC20.sol` contract and `IERC20.sol` interface in our contracts. To install the necessary dependencies, run the following command: ```bash npm install @nomicfoundation/hardhat-ethers ethers@6 @openzeppelin/contracts ``` ## Contract Setup {: #contracts } The following are the contracts that we'll be working with today: - `Batch.sol` - one of the precompile contracts on Moonbeam that allows you to combine multiple EVM calls into one. For more information on the available methods, please refer to the [Batch Solidity Interface](/builders/ethereum/precompiles/ux/batch/#the-batch-interface){target=\_blank} documentation - `DemoToken.sol` - an ERC-20 contract for the `DemoToken` (DTOK) token, which on deployment mints an initial supply and assigns them to the contract owner. It's a standard ERC-20 token, you can review the [IERC20 interface](https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#IERC20){target=\_blank} for more information on the available methods - `SimpleDex.sol` - a simple example of a DEX that on deployment deploys the `DemoToken` contract, which mints 1000 DTOKs, and allows you to swap DEV token for DTOKs and vice versa. **This contract is for demo purposes only**. The `SimpleDex` contract contains the following methods: - **token**() - a read-only method that returns the address of the `DemoToken` contract - **swapDevForDemoToken**() - a payable function that accepts DEV tokens in exchange for DTOK tokens. The function checks to make sure there are enough DTOK tokens held in the contract before making the transfer. After the transfer is made, a `Bought` event is emitted - **swapDemoTokenForDev**(*uint256* amount) - accepts the amount of DTOKs to swap for DEV tokens. The function checks to make sure the caller of the function has approved the contract to transfer their DTOKs before swapping the DTOKs back to DEV. After the transfer is made, a `Sold` event is emitted If you don't already have a `contracts` directory in your Hardhat project, you can create a new directory: ```bash mkdir contracts && cd contracts ``` Then, you can create a single file that we'll use to store the code for the `DemoToken` and `SimpleDex` contracts and another file for the batch precompile: ```bash touch SimpleDex.sol Batch.sol ``` In the `SimpleDex.sol` file, you can paste in the following code for the `DemoToken` and `SimpleDex` contracts: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract DemoToken is ERC20 { constructor(uint256 initialSupply) ERC20("DemoToken", "DTOK") { // Assign 500 DTOK tokens to the SimpleDex contract _mint(msg.sender, initialSupply / 2); // Assign 500 DTOK tokens to the EOA that deployed the SimpleDex contract _mint(tx.origin, initialSupply / 2); } } contract SimpleDex { IERC20 public token; event Bought(uint256 amount); event Sold(uint256 amount); // Make constructor payable so that DEV liquidity exists for the contract constructor() payable { // Mint 1000 DTOK tokens. Half will be assigned to the SimpleDex contract // and the other half will be assigned to the EOA that deployed the // SimpleDex contract token = new DemoToken(1000000000000000000000); } // Function to swap DEV for DTOK tokens function swapDevForDemoToken() payable public { // Verify the contract has enough tokens for the requested amount uint256 amountTobuy = msg.value; uint256 dexBalance = token.balanceOf(address(this)); require(amountTobuy > 0, "You need to send some DEV"); require(amountTobuy <= dexBalance, "Not enough tokens in the reserve"); // If enough, swap the DEV to DTOKs token.transfer(msg.sender, amountTobuy); emit Bought(amountTobuy); } // Function to swap DTOK for DEV tokens function swapDemoTokenForDev(uint256 amount) public { // Make sure the requested amount is greater than 0 and the caller // has approved the requested amount of tokens to be transferred require(amount > 0, "You need to sell at least some tokens"); uint256 allowance = token.allowance(msg.sender, address(this)); require(allowance >= amount, "Check the token allowance"); // Transfer the DTOKs to the contract token.transferFrom(msg.sender, address(this), amount); // Transfer the DEV tokens back to the caller payable(msg.sender).transfer(amount); emit Sold(amount); } } ``` In the `Batch.sol` file, you can paste in the Batch Precompile contract. ??? code "Batch.sol" ```solidity // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.3; /// @dev The Batch contract's address. address constant BATCH_ADDRESS = 0x0000000000000000000000000000000000000808; /// @dev The Batch contract's instance. Batch constant BATCH_CONTRACT = Batch(BATCH_ADDRESS); /// @author The Moonbeam Team /// @title Batch precompile /// @dev Allows to perform multiple calls through one call to the precompile. /// Can be used by EOA to do multiple calls in a single transaction. /// @custom:address 0x0000000000000000000000000000000000000808 interface Batch { /// @dev Batch multiple calls into a single transaction. /// All calls are performed from the address calling this precompile. /// /// In case of one subcall reverting following subcalls will still be attempted. /// /// @param to List of addresses to call. /// @param value List of values for each subcall. If array is shorter than "to" then additional /// calls will be performed with a value of 0. /// @param callData Call data for each `to` address. If array is shorter than "to" then /// additional calls will be performed with an empty call data. /// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas. /// If array is shorter than "to" then the remaining gas available will be used. /// @custom:selector 79df4b9c function batchSome( address[] memory to, uint256[] memory value, bytes[] memory callData, uint64[] memory gasLimit ) external; /// @dev Batch multiple calls into a single transaction. /// All calls are performed from the address calling this precompile. /// /// In case of one subcall reverting, no more subcalls will be executed but /// the batch transaction will succeed. Use batchAll to revert on any subcall revert. /// /// @param to List of addresses to call. /// @param value List of values for each subcall. If array is shorter than "to" then additional /// calls will be performed with a value of 0. /// @param callData Call data for each `to` address. If array is shorter than "to" then /// additional calls will be performed with an empty call data. /// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas. /// If array is shorter than "to" then the remaining gas available will be used. /// @custom:selector cf0491c7 function batchSomeUntilFailure( address[] memory to, uint256[] memory value, bytes[] memory callData, uint64[] memory gasLimit ) external; /// @dev Batch multiple calls into a single transaction. /// All calls are performed from the address calling this precompile. /// /// In case of one subcall reverting, the entire batch will revert. /// /// @param to List of addresses to call. /// @param value List of values for each subcall. If array is shorter than "to" then additional /// calls will be performed with a value of 0. /// @param callData Call data for each `to` address. If array is shorter than "to" then /// additional calls will be performed with an empty call data. /// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas. /// If array is shorter than "to" then the remaining gas available will be used. /// @custom:selector 96e292b8 function batchAll( address[] memory to, uint256[] memory value, bytes[] memory callData, uint64[] memory gasLimit ) external; /// Emitted when a subcall succeeds. event SubcallSucceeded(uint256 index); /// Emitted when a subcall fails. event SubcallFailed(uint256 index); } ``` ### Compile & Deploy Contracts {: #compile-deploy-contracts } To compile the contracts, we'll go ahead and run the following Hardhat command: ```bash npx hardhat compile ```
npx hardhat compile Compiled 6 Solidity files successfully (evm target: paris).
After compilation, an `artifacts` directory is created: it holds the bytecode and metadata of the contract, which are `.json` files. It’s a good idea to add this directory to the `.gitignore` file. Next, we can deploy the `SimpleDex` contract, which upon deployment will automatically deploy the `DemoToken` contract and mint 1000 DTOKs and assign half of them to the `SimpleDex` contract and the other half to the address that you're initiating the deployment from. We'll also add some initial liquidity to the contract by passing in a `value` when calling `deploy`. Since the value needs to be in Wei, we can use `ethers.parseEther` to pass in a value such as `"0.5"` DEV and it will convert the value to Wei for us. Before deploying the contract, we'll need to create the deployment script. We'll create a new directory for the script and name it `scripts` and add a new file to it called `deploy.js`: ```bash mkdir scripts && touch scripts/deploy.js ``` In the `deploy.js` script, you can paste in the following code, which will deploy the `SimpleDex` contract and print the address of the contract to the terminal upon successful deployment: ```js async function main() { // Liquidity to add in DEV (i.e., '.5') to be converted to Wei const value = ethers.parseEther('INSERT_AMOUNT_OF_DEV'); // Deploy the SimpleDex contract, which will also automatically deploy // the DemoToken contract and add liquidity to the contract const SimpleDex = await ethers.getContractFactory('SimpleDex',); const simpleDex = await SimpleDex.deploy({ value }) // Wait for the deployment transaction to be included in a block await simpleDex.waitForDeployment(); // Get and print the contract address const myContractDeployedAddress = await simpleDex.getAddress(); console.log(`SimpleDex deployed to ${myContractDeployedAddress}`); } main().catch((error) => { console.error(error); process.exitCode = 1; }); ``` Now we can deploy the `SimpleDex` contract using the `run` command and specifying `moonbase` as the network: ```bash npx hardhat run --network moonbase scripts/deploy.js ``` !!! note If you want to run the script in a standalone fashion using `node