diff --git a/.ai/categories/smart-contracts.md b/.ai/categories/smart-contracts.md index c3ad3ee60..0c06e7ff9 100644 --- a/.ai/categories/smart-contracts.md +++ b/.ai/categories/smart-contracts.md @@ -2366,8 +2366,6 @@ Page Title: Deploy Contracts to Polkadot Hub with Ethers.js # Ethers.js -!!! smartcontract "PolkaVM Preview Release" - PolkaVM smart contracts with Ethereum compatibility are in **early-stage development and may be unstable or incomplete**. ## Introduction [Ethers.js](https://docs.ethers.org/v6/){target=\_blank} is a lightweight library that enables interaction with Ethereum Virtual Machine (EVM)-compatible blockchains through JavaScript. Ethers is widely used as a toolkit to establish connections and read and write blockchain data. This article demonstrates using Ethers.js to interact and deploy smart contracts to Polkadot Hub. @@ -2399,7 +2397,7 @@ ethers-project ├── abis │ ├── Storage.json ├── artifacts -│ ├── Storage.polkavm +│ ├── Storage.bin ├── contract-address.json ├── node_modules/ ├── package.json @@ -2425,6 +2423,17 @@ Next, run the following command to install the Ethers.js library: npm install ethers ``` +Add the Solidity compiler so you can generate standard EVM bytecode: + +```bash +npm install --save-dev solc +``` + +This guide uses `solc` version `0.8.33`. + +!!! tip + The sample scripts use ECMAScript modules. Add `"type": "module"` to your `package.json` (or rename the files to `.mjs`) so that `node` can run the `import` statements. + ## Set Up the Ethers.js Provider A [`Provider`](https://docs.ethers.org/v6/api/providers/#Provider){target=\_blank} is an abstraction of a connection to the Ethereum network, allowing you to query blockchain data and send transactions. It serves as a bridge between your application and the blockchain. @@ -2467,12 +2476,12 @@ createProvider(PROVIDER_RPC.rpc, PROVIDER_RPC.chainId, PROVIDER_RPC.name); To connect to the provider, execute: ```bash -node connectToProvider +node scripts/connectToProvider.js ``` With the provider set up, you can start querying the blockchain. For instance, to fetch the latest block number: -??? code "Fetch Last Block code" +??? code "fetchLastBlock.js code" ```js title="scripts/fetchLastBlock.js" const { JsonRpcProvider } = require('ethers'); @@ -2512,22 +2521,7 @@ With the provider set up, you can start querying the blockchain. For instance, t ## Compile Contracts -!!! note "Contracts Code Blob Size Disclaimer" - The maximum contract code blob size on Polkadot Hub networks is _100 kilobytes_, significantly larger than Ethereum’s EVM limit of 24 kilobytes. - - For detailed comparisons and migration guidelines, see the [EVM vs. PolkaVM](/polkadot-protocol/smart-contract-basics/evm-vs-polkavm/#current-memory-limits){target=\_blank} documentation page. - -The `revive` compiler transforms Solidity smart contracts into [PolkaVM](/smart-contracts/overview/#native-smart-contracts){target=\_blank} bytecode for deployment on Polkadot Hub. Revive's Ethereum RPC interface allows you to use familiar tools like Ethers.js and MetaMask to interact with contracts. - -### Install the Revive Library - -The [`@parity/resolc`](https://www.npmjs.com/package/@parity/resolc){target=\_blank} library will compile your Solidity code for deployment on Polkadot Hub. Run the following command in your terminal to install the library: - -```bash -npm install --save-dev @parity/resolc -``` - -This guide uses `@parity/resolc` version `0.2.0`. +Polkadot Hub exposes an Ethereum JSON-RPC endpoint, so you can compile Solidity contracts to familiar EVM bytecode with the upstream [`solc`](https://www.npmjs.com/package/solc){target=\_blank} compiler. The resulting artifacts work with any EVM-compatible toolchain and can be deployed through Ethers.js. ### Sample Storage Smart Contract @@ -2562,40 +2556,74 @@ contract Storage { To compile this contract, use the following script: ```js title="scripts/compile.js" -const { compile } = require('@parity/resolc'); -const { readFileSync, writeFileSync } = require('fs'); +const solc = require('solc'); +const { readFileSync, writeFileSync, mkdirSync, existsSync } = require('fs'); const { basename, join } = require('path'); -const compileContract = async (solidityFilePath, outputDir) => { +const ensureDir = (dirPath) => { + if (!existsSync(dirPath)) { + mkdirSync(dirPath, { recursive: true }); + } +}; + +const compileContract = (solidityFilePath, abiDir, artifactsDir) => { try { // Read the Solidity file const source = readFileSync(solidityFilePath, 'utf8'); - - // Construct the input object for the compiler + const fileName = basename(solidityFilePath); + + // Construct the input object for the Solidity compiler const input = { - [basename(solidityFilePath)]: { content: source }, + language: 'Solidity', + sources: { + [fileName]: { + content: source, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['abi', 'evm.bytecode'], + }, + }, + }, }; - - console.log(`Compiling contract: ${basename(solidityFilePath)}...`); - + + console.log(`Compiling contract: ${fileName}...`); + // Compile the contract - const out = await compile(input); - - for (const contracts of Object.values(out.contracts)) { - for (const [name, contract] of Object.entries(contracts)) { - console.log(`Compiled contract: ${name}`); - + const output = JSON.parse(solc.compile(JSON.stringify(input))); + + // Check for errors + if (output.errors) { + const errors = output.errors.filter(error => error.severity === 'error'); + if (errors.length > 0) { + console.error('Compilation errors:'); + errors.forEach(err => console.error(err.formattedMessage)); + return; + } + // Show warnings + const warnings = output.errors.filter(error => error.severity === 'warning'); + warnings.forEach(warn => console.warn(warn.formattedMessage)); + } + + // Ensure output directories exist + ensureDir(abiDir); + ensureDir(artifactsDir); + + // Process compiled contracts + for (const [sourceFile, contracts] of Object.entries(output.contracts)) { + for (const [contractName, contract] of Object.entries(contracts)) { + console.log(`Compiled contract: ${contractName}`); + // Write the ABI - const abiPath = join(outputDir, `${name}.json`); + const abiPath = join(abiDir, `${contractName}.json`); writeFileSync(abiPath, JSON.stringify(contract.abi, null, 2)); console.log(`ABI saved to ${abiPath}`); - + // Write the bytecode - const bytecodePath = join(outputDir, `${name}.polkavm`); - writeFileSync( - bytecodePath, - Buffer.from(contract.evm.bytecode.object, 'hex'), - ); + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + writeFileSync(bytecodePath, contract.evm.bytecode.object); console.log(`Bytecode saved to ${bytecodePath}`); } } @@ -2605,10 +2633,10 @@ const compileContract = async (solidityFilePath, outputDir) => { }; const solidityFilePath = join(__dirname, '../contracts/Storage.sol'); -const outputDir = join(__dirname, '../contracts'); - -compileContract(solidityFilePath, outputDir); +const abiDir = join(__dirname, '../abis'); +const artifactsDir = join(__dirname, '../artifacts'); +compileContract(solidityFilePath, abiDir, artifactsDir); ``` !!! note @@ -2619,10 +2647,10 @@ The ABI (Application Binary Interface) is a JSON representation of your contract Execute the script above by running: ```bash -node compile +node scripts/compile.js ``` -After executing the script, the Solidity contract will be compiled into the required PolkaVM bytecode format. The ABI and bytecode will be saved into files with `.json` and `.polkavm` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. +After executing the script, the Solidity contract is compiled into standard EVM bytecode. The ABI and bytecode are saved into files with `.json` and `.bin` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. ## Deploy the Compiled Contract @@ -2633,19 +2661,17 @@ You can create a `deploy.js` script in the root of your project to achieve this. 1. Set up the required imports and utilities: ```js title="scripts/deploy.js" - // Deploy an EVM-compatible smart contract using ethers.js const { writeFileSync, existsSync, readFileSync } = require('fs'); const { join } = require('path'); const { ethers, JsonRpcProvider } = require('ethers'); - const codegenDir = join(__dirname); ``` 2. Create a provider to connect to Polkadot Hub: ```js title="scripts/deploy.js" - // Creates an Ethereum provider with specified RPC URL and chain details + // Creates a provider with specified RPC URL and chain details const createProvider = (rpcUrl, chainId, chainName) => { const provider = new JsonRpcProvider(rpcUrl, { chainId: chainId, @@ -2661,9 +2687,8 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads and parses the ABI file for a given contract const getAbi = (contractName) => { try { - return JSON.parse( - readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'), - ); + const abiPath = join(artifactsDir, `${contractName}.json`); + return JSON.parse(readFileSync(abiPath, 'utf8')); } catch (error) { console.error( `Could not find ABI for contract ${contractName}:`, @@ -2676,12 +2701,10 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads the compiled bytecode for a given contract const getByteCode = (contractName) => { try { - const bytecodePath = join( - codegenDir, - '../contracts', - `${contractName}.polkavm`, - ); - return `0x${readFileSync(bytecodePath).toString('hex')}`; + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + const bytecode = readFileSync(bytecodePath, 'utf8').trim(); + // Add 0x prefix if not present + return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; } catch (error) { console.error( `Could not find bytecode for contract ${contractName}:`, @@ -2690,15 +2713,14 @@ You can create a `deploy.js` script in the root of your project to achieve this. throw error; } }; + ``` 4. Create the main deployment function: ```js title="scripts/deploy.js" - const deployContract = async (contractName, mnemonic, providerConfig) => { console.log(`Deploying ${contractName}...`); - try { // Step 1: Set up provider and wallet const provider = createProvider( @@ -2722,10 +2744,11 @@ You can create a `deploy.js` script in the root of your project to achieve this. const address = await contract.getAddress(); console.log(`Contract ${contractName} deployed at: ${address}`); - const addressesFile = join(codegenDir, 'contract-address.json'); + const addressesFile = join(scriptsDir, 'contract-address.json'); const addresses = existsSync(addressesFile) ? JSON.parse(readFileSync(addressesFile, 'utf8')) : {}; + addresses[contractName] = address; writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8'); } catch (error) { @@ -2738,7 +2761,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. ```js title="scripts/deploy.js" const providerConfig = { - rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', + rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready chainId: 420420422, name: 'polkadot-hub-testnet', }; @@ -2756,14 +2779,14 @@ You can create a `deploy.js` script in the root of your project to achieve this. ??? code "View complete script" ```js title="scripts/deploy.js" - // Deploy an EVM-compatible smart contract using ethers.js const { writeFileSync, existsSync, readFileSync } = require('fs'); const { join } = require('path'); const { ethers, JsonRpcProvider } = require('ethers'); - const codegenDir = join(__dirname); + const scriptsDir = __dirname; + const artifactsDir = join(__dirname, '../contracts'); - // Creates an Ethereum provider with specified RPC URL and chain details + // Creates a provider with specified RPC URL and chain details const createProvider = (rpcUrl, chainId, chainName) => { const provider = new JsonRpcProvider(rpcUrl, { chainId: chainId, @@ -2775,9 +2798,8 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads and parses the ABI file for a given contract const getAbi = (contractName) => { try { - return JSON.parse( - readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'), - ); + const abiPath = join(artifactsDir, `${contractName}.json`); + return JSON.parse(readFileSync(abiPath, 'utf8')); } catch (error) { console.error( `Could not find ABI for contract ${contractName}:`, @@ -2790,12 +2812,10 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads the compiled bytecode for a given contract const getByteCode = (contractName) => { try { - const bytecodePath = join( - codegenDir, - '../contracts', - `${contractName}.polkavm`, - ); - return `0x${readFileSync(bytecodePath).toString('hex')}`; + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + const bytecode = readFileSync(bytecodePath, 'utf8').trim(); + // Add 0x prefix if not present + return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; } catch (error) { console.error( `Could not find bytecode for contract ${contractName}:`, @@ -2807,7 +2827,6 @@ You can create a `deploy.js` script in the root of your project to achieve this. const deployContract = async (contractName, mnemonic, providerConfig) => { console.log(`Deploying ${contractName}...`); - try { // Step 1: Set up provider and wallet const provider = createProvider( @@ -2831,10 +2850,11 @@ You can create a `deploy.js` script in the root of your project to achieve this. const address = await contract.getAddress(); console.log(`Contract ${contractName} deployed at: ${address}`); - const addressesFile = join(codegenDir, 'contract-address.json'); + const addressesFile = join(scriptsDir, 'contract-address.json'); const addresses = existsSync(addressesFile) ? JSON.parse(readFileSync(addressesFile, 'utf8')) : {}; + addresses[contractName] = address; writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8'); } catch (error) { @@ -2843,7 +2863,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. }; const providerConfig = { - rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', + rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready chainId: 420420422, name: 'polkadot-hub-testnet', }; @@ -2851,13 +2871,12 @@ You can create a `deploy.js` script in the root of your project to achieve this. const mnemonic = 'INSERT_MNEMONIC'; deployContract('Storage', mnemonic, providerConfig); - ``` To run the script, execute the following command: ```bash -node deploy +node scripts/deploy.js ``` After running this script, your contract will be deployed to Polkadot Hub, and its address will be saved in `contract-address.json` within your project directory. You can use this address for future contract interactions. @@ -2871,6 +2890,8 @@ const { ethers } = require('ethers'); const { readFileSync } = require('fs'); const { join } = require('path'); +const artifactsDir = join(__dirname, '../contracts'); + const createProvider = (providerConfig) => { return new ethers.JsonRpcProvider(providerConfig.rpc, { chainId: providerConfig.chainId, @@ -2882,7 +2903,7 @@ const createWallet = (mnemonic, provider) => { return ethers.Wallet.fromPhrase(mnemonic).connect(provider); }; -const loadContractAbi = (contractName, directory = __dirname) => { +const loadContractAbi = (contractName, directory = artifactsDir) => { const contractPath = join(directory, `${contractName}.json`); const contractJson = JSON.parse(readFileSync(contractPath, 'utf8')); return contractJson.abi || contractJson; // Depending on JSON structure @@ -2938,9 +2959,9 @@ const providerConfig = { chainId: 420420422, }; -const mnemonic = 'INSERT_MNEMONIC'; +const mnemonic = 'INSERT_MNEMONIC' const contractName = 'Storage'; -const contractAddress = 'INSERT_CONTRACT_ADDRESS'; +const contractAddress = 'INSERT_CONTRACT_ADDRESS' const newNumber = 42; interactWithStorageContract( @@ -2950,15 +2971,14 @@ interactWithStorageContract( providerConfig, newNumber, ); - ``` -Ensure you replace the `INSERT_MNEMONIC`, `INSERT_CONTRACT_ADDRESS`, and `INSERT_ADDRESS_TO_CHECK` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. +Ensure you replace the `INSERT_MNEMONIC` and `INSERT_CONTRACT_ADDRESS` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. The script prints the balance for `ADDRESS_TO_CHECK` before it writes and doubles the stored value, so pick any account you want to monitor. To interact with the contract, run: ```bash -node checkStorage +node scripts/checkStorage.js ``` ## Where to Go Next diff --git a/.ai/categories/tooling.md b/.ai/categories/tooling.md index a5d3fcb87..db867e5c0 100644 --- a/.ai/categories/tooling.md +++ b/.ai/categories/tooling.md @@ -1412,8 +1412,6 @@ Page Title: Deploy Contracts to Polkadot Hub with Ethers.js # Ethers.js -!!! smartcontract "PolkaVM Preview Release" - PolkaVM smart contracts with Ethereum compatibility are in **early-stage development and may be unstable or incomplete**. ## Introduction [Ethers.js](https://docs.ethers.org/v6/){target=\_blank} is a lightweight library that enables interaction with Ethereum Virtual Machine (EVM)-compatible blockchains through JavaScript. Ethers is widely used as a toolkit to establish connections and read and write blockchain data. This article demonstrates using Ethers.js to interact and deploy smart contracts to Polkadot Hub. @@ -1445,7 +1443,7 @@ ethers-project ├── abis │ ├── Storage.json ├── artifacts -│ ├── Storage.polkavm +│ ├── Storage.bin ├── contract-address.json ├── node_modules/ ├── package.json @@ -1471,6 +1469,17 @@ Next, run the following command to install the Ethers.js library: npm install ethers ``` +Add the Solidity compiler so you can generate standard EVM bytecode: + +```bash +npm install --save-dev solc +``` + +This guide uses `solc` version `0.8.33`. + +!!! tip + The sample scripts use ECMAScript modules. Add `"type": "module"` to your `package.json` (or rename the files to `.mjs`) so that `node` can run the `import` statements. + ## Set Up the Ethers.js Provider A [`Provider`](https://docs.ethers.org/v6/api/providers/#Provider){target=\_blank} is an abstraction of a connection to the Ethereum network, allowing you to query blockchain data and send transactions. It serves as a bridge between your application and the blockchain. @@ -1513,12 +1522,12 @@ createProvider(PROVIDER_RPC.rpc, PROVIDER_RPC.chainId, PROVIDER_RPC.name); To connect to the provider, execute: ```bash -node connectToProvider +node scripts/connectToProvider.js ``` With the provider set up, you can start querying the blockchain. For instance, to fetch the latest block number: -??? code "Fetch Last Block code" +??? code "fetchLastBlock.js code" ```js title="scripts/fetchLastBlock.js" const { JsonRpcProvider } = require('ethers'); @@ -1558,22 +1567,7 @@ With the provider set up, you can start querying the blockchain. For instance, t ## Compile Contracts -!!! note "Contracts Code Blob Size Disclaimer" - The maximum contract code blob size on Polkadot Hub networks is _100 kilobytes_, significantly larger than Ethereum’s EVM limit of 24 kilobytes. - - For detailed comparisons and migration guidelines, see the [EVM vs. PolkaVM](/polkadot-protocol/smart-contract-basics/evm-vs-polkavm/#current-memory-limits){target=\_blank} documentation page. - -The `revive` compiler transforms Solidity smart contracts into [PolkaVM](/smart-contracts/overview/#native-smart-contracts){target=\_blank} bytecode for deployment on Polkadot Hub. Revive's Ethereum RPC interface allows you to use familiar tools like Ethers.js and MetaMask to interact with contracts. - -### Install the Revive Library - -The [`@parity/resolc`](https://www.npmjs.com/package/@parity/resolc){target=\_blank} library will compile your Solidity code for deployment on Polkadot Hub. Run the following command in your terminal to install the library: - -```bash -npm install --save-dev @parity/resolc -``` - -This guide uses `@parity/resolc` version `0.2.0`. +Polkadot Hub exposes an Ethereum JSON-RPC endpoint, so you can compile Solidity contracts to familiar EVM bytecode with the upstream [`solc`](https://www.npmjs.com/package/solc){target=\_blank} compiler. The resulting artifacts work with any EVM-compatible toolchain and can be deployed through Ethers.js. ### Sample Storage Smart Contract @@ -1608,40 +1602,74 @@ contract Storage { To compile this contract, use the following script: ```js title="scripts/compile.js" -const { compile } = require('@parity/resolc'); -const { readFileSync, writeFileSync } = require('fs'); +const solc = require('solc'); +const { readFileSync, writeFileSync, mkdirSync, existsSync } = require('fs'); const { basename, join } = require('path'); -const compileContract = async (solidityFilePath, outputDir) => { +const ensureDir = (dirPath) => { + if (!existsSync(dirPath)) { + mkdirSync(dirPath, { recursive: true }); + } +}; + +const compileContract = (solidityFilePath, abiDir, artifactsDir) => { try { // Read the Solidity file const source = readFileSync(solidityFilePath, 'utf8'); - - // Construct the input object for the compiler + const fileName = basename(solidityFilePath); + + // Construct the input object for the Solidity compiler const input = { - [basename(solidityFilePath)]: { content: source }, + language: 'Solidity', + sources: { + [fileName]: { + content: source, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['abi', 'evm.bytecode'], + }, + }, + }, }; - - console.log(`Compiling contract: ${basename(solidityFilePath)}...`); - + + console.log(`Compiling contract: ${fileName}...`); + // Compile the contract - const out = await compile(input); - - for (const contracts of Object.values(out.contracts)) { - for (const [name, contract] of Object.entries(contracts)) { - console.log(`Compiled contract: ${name}`); - + const output = JSON.parse(solc.compile(JSON.stringify(input))); + + // Check for errors + if (output.errors) { + const errors = output.errors.filter(error => error.severity === 'error'); + if (errors.length > 0) { + console.error('Compilation errors:'); + errors.forEach(err => console.error(err.formattedMessage)); + return; + } + // Show warnings + const warnings = output.errors.filter(error => error.severity === 'warning'); + warnings.forEach(warn => console.warn(warn.formattedMessage)); + } + + // Ensure output directories exist + ensureDir(abiDir); + ensureDir(artifactsDir); + + // Process compiled contracts + for (const [sourceFile, contracts] of Object.entries(output.contracts)) { + for (const [contractName, contract] of Object.entries(contracts)) { + console.log(`Compiled contract: ${contractName}`); + // Write the ABI - const abiPath = join(outputDir, `${name}.json`); + const abiPath = join(abiDir, `${contractName}.json`); writeFileSync(abiPath, JSON.stringify(contract.abi, null, 2)); console.log(`ABI saved to ${abiPath}`); - + // Write the bytecode - const bytecodePath = join(outputDir, `${name}.polkavm`); - writeFileSync( - bytecodePath, - Buffer.from(contract.evm.bytecode.object, 'hex'), - ); + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + writeFileSync(bytecodePath, contract.evm.bytecode.object); console.log(`Bytecode saved to ${bytecodePath}`); } } @@ -1651,10 +1679,10 @@ const compileContract = async (solidityFilePath, outputDir) => { }; const solidityFilePath = join(__dirname, '../contracts/Storage.sol'); -const outputDir = join(__dirname, '../contracts'); - -compileContract(solidityFilePath, outputDir); +const abiDir = join(__dirname, '../abis'); +const artifactsDir = join(__dirname, '../artifacts'); +compileContract(solidityFilePath, abiDir, artifactsDir); ``` !!! note @@ -1665,10 +1693,10 @@ The ABI (Application Binary Interface) is a JSON representation of your contract Execute the script above by running: ```bash -node compile +node scripts/compile.js ``` -After executing the script, the Solidity contract will be compiled into the required PolkaVM bytecode format. The ABI and bytecode will be saved into files with `.json` and `.polkavm` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. +After executing the script, the Solidity contract is compiled into standard EVM bytecode. The ABI and bytecode are saved into files with `.json` and `.bin` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. ## Deploy the Compiled Contract @@ -1679,19 +1707,17 @@ You can create a `deploy.js` script in the root of your project to achieve this. 1. Set up the required imports and utilities: ```js title="scripts/deploy.js" - // Deploy an EVM-compatible smart contract using ethers.js const { writeFileSync, existsSync, readFileSync } = require('fs'); const { join } = require('path'); const { ethers, JsonRpcProvider } = require('ethers'); - const codegenDir = join(__dirname); ``` 2. Create a provider to connect to Polkadot Hub: ```js title="scripts/deploy.js" - // Creates an Ethereum provider with specified RPC URL and chain details + // Creates a provider with specified RPC URL and chain details const createProvider = (rpcUrl, chainId, chainName) => { const provider = new JsonRpcProvider(rpcUrl, { chainId: chainId, @@ -1707,9 +1733,8 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads and parses the ABI file for a given contract const getAbi = (contractName) => { try { - return JSON.parse( - readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'), - ); + const abiPath = join(artifactsDir, `${contractName}.json`); + return JSON.parse(readFileSync(abiPath, 'utf8')); } catch (error) { console.error( `Could not find ABI for contract ${contractName}:`, @@ -1722,12 +1747,10 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads the compiled bytecode for a given contract const getByteCode = (contractName) => { try { - const bytecodePath = join( - codegenDir, - '../contracts', - `${contractName}.polkavm`, - ); - return `0x${readFileSync(bytecodePath).toString('hex')}`; + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + const bytecode = readFileSync(bytecodePath, 'utf8').trim(); + // Add 0x prefix if not present + return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; } catch (error) { console.error( `Could not find bytecode for contract ${contractName}:`, @@ -1736,15 +1759,14 @@ You can create a `deploy.js` script in the root of your project to achieve this. throw error; } }; + ``` 4. Create the main deployment function: ```js title="scripts/deploy.js" - const deployContract = async (contractName, mnemonic, providerConfig) => { console.log(`Deploying ${contractName}...`); - try { // Step 1: Set up provider and wallet const provider = createProvider( @@ -1768,10 +1790,11 @@ You can create a `deploy.js` script in the root of your project to achieve this. const address = await contract.getAddress(); console.log(`Contract ${contractName} deployed at: ${address}`); - const addressesFile = join(codegenDir, 'contract-address.json'); + const addressesFile = join(scriptsDir, 'contract-address.json'); const addresses = existsSync(addressesFile) ? JSON.parse(readFileSync(addressesFile, 'utf8')) : {}; + addresses[contractName] = address; writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8'); } catch (error) { @@ -1784,7 +1807,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. ```js title="scripts/deploy.js" const providerConfig = { - rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', + rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready chainId: 420420422, name: 'polkadot-hub-testnet', }; @@ -1802,14 +1825,14 @@ You can create a `deploy.js` script in the root of your project to achieve this. ??? code "View complete script" ```js title="scripts/deploy.js" - // Deploy an EVM-compatible smart contract using ethers.js const { writeFileSync, existsSync, readFileSync } = require('fs'); const { join } = require('path'); const { ethers, JsonRpcProvider } = require('ethers'); - const codegenDir = join(__dirname); + const scriptsDir = __dirname; + const artifactsDir = join(__dirname, '../contracts'); - // Creates an Ethereum provider with specified RPC URL and chain details + // Creates a provider with specified RPC URL and chain details const createProvider = (rpcUrl, chainId, chainName) => { const provider = new JsonRpcProvider(rpcUrl, { chainId: chainId, @@ -1821,9 +1844,8 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads and parses the ABI file for a given contract const getAbi = (contractName) => { try { - return JSON.parse( - readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'), - ); + const abiPath = join(artifactsDir, `${contractName}.json`); + return JSON.parse(readFileSync(abiPath, 'utf8')); } catch (error) { console.error( `Could not find ABI for contract ${contractName}:`, @@ -1836,12 +1858,10 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads the compiled bytecode for a given contract const getByteCode = (contractName) => { try { - const bytecodePath = join( - codegenDir, - '../contracts', - `${contractName}.polkavm`, - ); - return `0x${readFileSync(bytecodePath).toString('hex')}`; + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + const bytecode = readFileSync(bytecodePath, 'utf8').trim(); + // Add 0x prefix if not present + return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; } catch (error) { console.error( `Could not find bytecode for contract ${contractName}:`, @@ -1853,7 +1873,6 @@ You can create a `deploy.js` script in the root of your project to achieve this. const deployContract = async (contractName, mnemonic, providerConfig) => { console.log(`Deploying ${contractName}...`); - try { // Step 1: Set up provider and wallet const provider = createProvider( @@ -1877,10 +1896,11 @@ You can create a `deploy.js` script in the root of your project to achieve this. const address = await contract.getAddress(); console.log(`Contract ${contractName} deployed at: ${address}`); - const addressesFile = join(codegenDir, 'contract-address.json'); + const addressesFile = join(scriptsDir, 'contract-address.json'); const addresses = existsSync(addressesFile) ? JSON.parse(readFileSync(addressesFile, 'utf8')) : {}; + addresses[contractName] = address; writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8'); } catch (error) { @@ -1889,7 +1909,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. }; const providerConfig = { - rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', + rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready chainId: 420420422, name: 'polkadot-hub-testnet', }; @@ -1897,13 +1917,12 @@ You can create a `deploy.js` script in the root of your project to achieve this. const mnemonic = 'INSERT_MNEMONIC'; deployContract('Storage', mnemonic, providerConfig); - ``` To run the script, execute the following command: ```bash -node deploy +node scripts/deploy.js ``` After running this script, your contract will be deployed to Polkadot Hub, and its address will be saved in `contract-address.json` within your project directory. You can use this address for future contract interactions. @@ -1917,6 +1936,8 @@ const { ethers } = require('ethers'); const { readFileSync } = require('fs'); const { join } = require('path'); +const artifactsDir = join(__dirname, '../contracts'); + const createProvider = (providerConfig) => { return new ethers.JsonRpcProvider(providerConfig.rpc, { chainId: providerConfig.chainId, @@ -1928,7 +1949,7 @@ const createWallet = (mnemonic, provider) => { return ethers.Wallet.fromPhrase(mnemonic).connect(provider); }; -const loadContractAbi = (contractName, directory = __dirname) => { +const loadContractAbi = (contractName, directory = artifactsDir) => { const contractPath = join(directory, `${contractName}.json`); const contractJson = JSON.parse(readFileSync(contractPath, 'utf8')); return contractJson.abi || contractJson; // Depending on JSON structure @@ -1984,9 +2005,9 @@ const providerConfig = { chainId: 420420422, }; -const mnemonic = 'INSERT_MNEMONIC'; +const mnemonic = 'INSERT_MNEMONIC' const contractName = 'Storage'; -const contractAddress = 'INSERT_CONTRACT_ADDRESS'; +const contractAddress = 'INSERT_CONTRACT_ADDRESS' const newNumber = 42; interactWithStorageContract( @@ -1996,15 +2017,14 @@ interactWithStorageContract( providerConfig, newNumber, ); - ``` -Ensure you replace the `INSERT_MNEMONIC`, `INSERT_CONTRACT_ADDRESS`, and `INSERT_ADDRESS_TO_CHECK` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. +Ensure you replace the `INSERT_MNEMONIC` and `INSERT_CONTRACT_ADDRESS` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. The script prints the balance for `ADDRESS_TO_CHECK` before it writes and doubles the stored value, so pick any account you want to monitor. To interact with the contract, run: ```bash -node checkStorage +node scripts/checkStorage.js ``` ## Where to Go Next diff --git a/.ai/pages/smart-contracts-libraries-ethers-js.md b/.ai/pages/smart-contracts-libraries-ethers-js.md index 5983b83be..a12b531a0 100644 --- a/.ai/pages/smart-contracts-libraries-ethers-js.md +++ b/.ai/pages/smart-contracts-libraries-ethers-js.md @@ -7,8 +7,6 @@ url: https://docs.polkadot.com/smart-contracts/libraries/ethers-js/ # Ethers.js -!!! smartcontract "PolkaVM Preview Release" - PolkaVM smart contracts with Ethereum compatibility are in **early-stage development and may be unstable or incomplete**. ## Introduction [Ethers.js](https://docs.ethers.org/v6/){target=\_blank} is a lightweight library that enables interaction with Ethereum Virtual Machine (EVM)-compatible blockchains through JavaScript. Ethers is widely used as a toolkit to establish connections and read and write blockchain data. This article demonstrates using Ethers.js to interact and deploy smart contracts to Polkadot Hub. @@ -40,7 +38,7 @@ ethers-project ├── abis │ ├── Storage.json ├── artifacts -│ ├── Storage.polkavm +│ ├── Storage.bin ├── contract-address.json ├── node_modules/ ├── package.json @@ -66,6 +64,17 @@ Next, run the following command to install the Ethers.js library: npm install ethers ``` +Add the Solidity compiler so you can generate standard EVM bytecode: + +```bash +npm install --save-dev solc +``` + +This guide uses `solc` version `0.8.33`. + +!!! tip + The sample scripts use ECMAScript modules. Add `"type": "module"` to your `package.json` (or rename the files to `.mjs`) so that `node` can run the `import` statements. + ## Set Up the Ethers.js Provider A [`Provider`](https://docs.ethers.org/v6/api/providers/#Provider){target=\_blank} is an abstraction of a connection to the Ethereum network, allowing you to query blockchain data and send transactions. It serves as a bridge between your application and the blockchain. @@ -108,12 +117,12 @@ createProvider(PROVIDER_RPC.rpc, PROVIDER_RPC.chainId, PROVIDER_RPC.name); To connect to the provider, execute: ```bash -node connectToProvider +node scripts/connectToProvider.js ``` With the provider set up, you can start querying the blockchain. For instance, to fetch the latest block number: -??? code "Fetch Last Block code" +??? code "fetchLastBlock.js code" ```js title="scripts/fetchLastBlock.js" const { JsonRpcProvider } = require('ethers'); @@ -153,22 +162,7 @@ With the provider set up, you can start querying the blockchain. For instance, t ## Compile Contracts -!!! note "Contracts Code Blob Size Disclaimer" - The maximum contract code blob size on Polkadot Hub networks is _100 kilobytes_, significantly larger than Ethereum’s EVM limit of 24 kilobytes. - - For detailed comparisons and migration guidelines, see the [EVM vs. PolkaVM](/polkadot-protocol/smart-contract-basics/evm-vs-polkavm/#current-memory-limits){target=\_blank} documentation page. - -The `revive` compiler transforms Solidity smart contracts into [PolkaVM](/smart-contracts/overview/#native-smart-contracts){target=\_blank} bytecode for deployment on Polkadot Hub. Revive's Ethereum RPC interface allows you to use familiar tools like Ethers.js and MetaMask to interact with contracts. - -### Install the Revive Library - -The [`@parity/resolc`](https://www.npmjs.com/package/@parity/resolc){target=\_blank} library will compile your Solidity code for deployment on Polkadot Hub. Run the following command in your terminal to install the library: - -```bash -npm install --save-dev @parity/resolc -``` - -This guide uses `@parity/resolc` version `0.2.0`. +Polkadot Hub exposes an Ethereum JSON-RPC endpoint, so you can compile Solidity contracts to familiar EVM bytecode with the upstream [`solc`](https://www.npmjs.com/package/solc){target=\_blank} compiler. The resulting artifacts work with any EVM-compatible toolchain and can be deployed through Ethers.js. ### Sample Storage Smart Contract @@ -203,40 +197,74 @@ contract Storage { To compile this contract, use the following script: ```js title="scripts/compile.js" -const { compile } = require('@parity/resolc'); -const { readFileSync, writeFileSync } = require('fs'); +const solc = require('solc'); +const { readFileSync, writeFileSync, mkdirSync, existsSync } = require('fs'); const { basename, join } = require('path'); -const compileContract = async (solidityFilePath, outputDir) => { +const ensureDir = (dirPath) => { + if (!existsSync(dirPath)) { + mkdirSync(dirPath, { recursive: true }); + } +}; + +const compileContract = (solidityFilePath, abiDir, artifactsDir) => { try { // Read the Solidity file const source = readFileSync(solidityFilePath, 'utf8'); - - // Construct the input object for the compiler + const fileName = basename(solidityFilePath); + + // Construct the input object for the Solidity compiler const input = { - [basename(solidityFilePath)]: { content: source }, + language: 'Solidity', + sources: { + [fileName]: { + content: source, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['abi', 'evm.bytecode'], + }, + }, + }, }; - - console.log(`Compiling contract: ${basename(solidityFilePath)}...`); - + + console.log(`Compiling contract: ${fileName}...`); + // Compile the contract - const out = await compile(input); - - for (const contracts of Object.values(out.contracts)) { - for (const [name, contract] of Object.entries(contracts)) { - console.log(`Compiled contract: ${name}`); - + const output = JSON.parse(solc.compile(JSON.stringify(input))); + + // Check for errors + if (output.errors) { + const errors = output.errors.filter(error => error.severity === 'error'); + if (errors.length > 0) { + console.error('Compilation errors:'); + errors.forEach(err => console.error(err.formattedMessage)); + return; + } + // Show warnings + const warnings = output.errors.filter(error => error.severity === 'warning'); + warnings.forEach(warn => console.warn(warn.formattedMessage)); + } + + // Ensure output directories exist + ensureDir(abiDir); + ensureDir(artifactsDir); + + // Process compiled contracts + for (const [sourceFile, contracts] of Object.entries(output.contracts)) { + for (const [contractName, contract] of Object.entries(contracts)) { + console.log(`Compiled contract: ${contractName}`); + // Write the ABI - const abiPath = join(outputDir, `${name}.json`); + const abiPath = join(abiDir, `${contractName}.json`); writeFileSync(abiPath, JSON.stringify(contract.abi, null, 2)); console.log(`ABI saved to ${abiPath}`); - + // Write the bytecode - const bytecodePath = join(outputDir, `${name}.polkavm`); - writeFileSync( - bytecodePath, - Buffer.from(contract.evm.bytecode.object, 'hex'), - ); + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + writeFileSync(bytecodePath, contract.evm.bytecode.object); console.log(`Bytecode saved to ${bytecodePath}`); } } @@ -246,10 +274,10 @@ const compileContract = async (solidityFilePath, outputDir) => { }; const solidityFilePath = join(__dirname, '../contracts/Storage.sol'); -const outputDir = join(__dirname, '../contracts'); - -compileContract(solidityFilePath, outputDir); +const abiDir = join(__dirname, '../abis'); +const artifactsDir = join(__dirname, '../artifacts'); +compileContract(solidityFilePath, abiDir, artifactsDir); ``` !!! note @@ -260,10 +288,10 @@ The ABI (Application Binary Interface) is a JSON representation of your contract Execute the script above by running: ```bash -node compile +node scripts/compile.js ``` -After executing the script, the Solidity contract will be compiled into the required PolkaVM bytecode format. The ABI and bytecode will be saved into files with `.json` and `.polkavm` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. +After executing the script, the Solidity contract is compiled into standard EVM bytecode. The ABI and bytecode are saved into files with `.json` and `.bin` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. ## Deploy the Compiled Contract @@ -274,19 +302,17 @@ You can create a `deploy.js` script in the root of your project to achieve this. 1. Set up the required imports and utilities: ```js title="scripts/deploy.js" - // Deploy an EVM-compatible smart contract using ethers.js const { writeFileSync, existsSync, readFileSync } = require('fs'); const { join } = require('path'); const { ethers, JsonRpcProvider } = require('ethers'); - const codegenDir = join(__dirname); ``` 2. Create a provider to connect to Polkadot Hub: ```js title="scripts/deploy.js" - // Creates an Ethereum provider with specified RPC URL and chain details + // Creates a provider with specified RPC URL and chain details const createProvider = (rpcUrl, chainId, chainName) => { const provider = new JsonRpcProvider(rpcUrl, { chainId: chainId, @@ -302,9 +328,8 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads and parses the ABI file for a given contract const getAbi = (contractName) => { try { - return JSON.parse( - readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'), - ); + const abiPath = join(artifactsDir, `${contractName}.json`); + return JSON.parse(readFileSync(abiPath, 'utf8')); } catch (error) { console.error( `Could not find ABI for contract ${contractName}:`, @@ -317,12 +342,10 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads the compiled bytecode for a given contract const getByteCode = (contractName) => { try { - const bytecodePath = join( - codegenDir, - '../contracts', - `${contractName}.polkavm`, - ); - return `0x${readFileSync(bytecodePath).toString('hex')}`; + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + const bytecode = readFileSync(bytecodePath, 'utf8').trim(); + // Add 0x prefix if not present + return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; } catch (error) { console.error( `Could not find bytecode for contract ${contractName}:`, @@ -331,15 +354,14 @@ You can create a `deploy.js` script in the root of your project to achieve this. throw error; } }; + ``` 4. Create the main deployment function: ```js title="scripts/deploy.js" - const deployContract = async (contractName, mnemonic, providerConfig) => { console.log(`Deploying ${contractName}...`); - try { // Step 1: Set up provider and wallet const provider = createProvider( @@ -363,10 +385,11 @@ You can create a `deploy.js` script in the root of your project to achieve this. const address = await contract.getAddress(); console.log(`Contract ${contractName} deployed at: ${address}`); - const addressesFile = join(codegenDir, 'contract-address.json'); + const addressesFile = join(scriptsDir, 'contract-address.json'); const addresses = existsSync(addressesFile) ? JSON.parse(readFileSync(addressesFile, 'utf8')) : {}; + addresses[contractName] = address; writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8'); } catch (error) { @@ -379,7 +402,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. ```js title="scripts/deploy.js" const providerConfig = { - rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', + rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready chainId: 420420422, name: 'polkadot-hub-testnet', }; @@ -397,14 +420,14 @@ You can create a `deploy.js` script in the root of your project to achieve this. ??? code "View complete script" ```js title="scripts/deploy.js" - // Deploy an EVM-compatible smart contract using ethers.js const { writeFileSync, existsSync, readFileSync } = require('fs'); const { join } = require('path'); const { ethers, JsonRpcProvider } = require('ethers'); - const codegenDir = join(__dirname); + const scriptsDir = __dirname; + const artifactsDir = join(__dirname, '../contracts'); - // Creates an Ethereum provider with specified RPC URL and chain details + // Creates a provider with specified RPC URL and chain details const createProvider = (rpcUrl, chainId, chainName) => { const provider = new JsonRpcProvider(rpcUrl, { chainId: chainId, @@ -416,9 +439,8 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads and parses the ABI file for a given contract const getAbi = (contractName) => { try { - return JSON.parse( - readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'), - ); + const abiPath = join(artifactsDir, `${contractName}.json`); + return JSON.parse(readFileSync(abiPath, 'utf8')); } catch (error) { console.error( `Could not find ABI for contract ${contractName}:`, @@ -431,12 +453,10 @@ You can create a `deploy.js` script in the root of your project to achieve this. // Reads the compiled bytecode for a given contract const getByteCode = (contractName) => { try { - const bytecodePath = join( - codegenDir, - '../contracts', - `${contractName}.polkavm`, - ); - return `0x${readFileSync(bytecodePath).toString('hex')}`; + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + const bytecode = readFileSync(bytecodePath, 'utf8').trim(); + // Add 0x prefix if not present + return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; } catch (error) { console.error( `Could not find bytecode for contract ${contractName}:`, @@ -448,7 +468,6 @@ You can create a `deploy.js` script in the root of your project to achieve this. const deployContract = async (contractName, mnemonic, providerConfig) => { console.log(`Deploying ${contractName}...`); - try { // Step 1: Set up provider and wallet const provider = createProvider( @@ -472,10 +491,11 @@ You can create a `deploy.js` script in the root of your project to achieve this. const address = await contract.getAddress(); console.log(`Contract ${contractName} deployed at: ${address}`); - const addressesFile = join(codegenDir, 'contract-address.json'); + const addressesFile = join(scriptsDir, 'contract-address.json'); const addresses = existsSync(addressesFile) ? JSON.parse(readFileSync(addressesFile, 'utf8')) : {}; + addresses[contractName] = address; writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8'); } catch (error) { @@ -484,7 +504,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. }; const providerConfig = { - rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', + rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready chainId: 420420422, name: 'polkadot-hub-testnet', }; @@ -492,13 +512,12 @@ You can create a `deploy.js` script in the root of your project to achieve this. const mnemonic = 'INSERT_MNEMONIC'; deployContract('Storage', mnemonic, providerConfig); - ``` To run the script, execute the following command: ```bash -node deploy +node scripts/deploy.js ``` After running this script, your contract will be deployed to Polkadot Hub, and its address will be saved in `contract-address.json` within your project directory. You can use this address for future contract interactions. @@ -512,6 +531,8 @@ const { ethers } = require('ethers'); const { readFileSync } = require('fs'); const { join } = require('path'); +const artifactsDir = join(__dirname, '../contracts'); + const createProvider = (providerConfig) => { return new ethers.JsonRpcProvider(providerConfig.rpc, { chainId: providerConfig.chainId, @@ -523,7 +544,7 @@ const createWallet = (mnemonic, provider) => { return ethers.Wallet.fromPhrase(mnemonic).connect(provider); }; -const loadContractAbi = (contractName, directory = __dirname) => { +const loadContractAbi = (contractName, directory = artifactsDir) => { const contractPath = join(directory, `${contractName}.json`); const contractJson = JSON.parse(readFileSync(contractPath, 'utf8')); return contractJson.abi || contractJson; // Depending on JSON structure @@ -579,9 +600,9 @@ const providerConfig = { chainId: 420420422, }; -const mnemonic = 'INSERT_MNEMONIC'; +const mnemonic = 'INSERT_MNEMONIC' const contractName = 'Storage'; -const contractAddress = 'INSERT_CONTRACT_ADDRESS'; +const contractAddress = 'INSERT_CONTRACT_ADDRESS' const newNumber = 42; interactWithStorageContract( @@ -591,15 +612,14 @@ interactWithStorageContract( providerConfig, newNumber, ); - ``` -Ensure you replace the `INSERT_MNEMONIC`, `INSERT_CONTRACT_ADDRESS`, and `INSERT_ADDRESS_TO_CHECK` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. +Ensure you replace the `INSERT_MNEMONIC` and `INSERT_CONTRACT_ADDRESS` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. The script prints the balance for `ADDRESS_TO_CHECK` before it writes and doubles the stored value, so pick any account you want to monitor. To interact with the contract, run: ```bash -node checkStorage +node scripts/checkStorage.js ``` ## Where to Go Next diff --git a/.ai/site-index.json b/.ai/site-index.json index 089f9ad41..58107d5b7 100644 --- a/.ai/site-index.json +++ b/.ai/site-index.json @@ -4966,7 +4966,7 @@ ], "raw_md_url": "https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.ai/pages/smart-contracts-libraries-ethers-js.md", "html_url": "https://docs.polkadot.com/smart-contracts/libraries/ethers-js/", - "preview": "!!! smartcontract \"PolkaVM Preview Release\" PolkaVM smart contracts with Ethereum compatibility are in **early-stage development and may be unstable or incomplete**. ## Introduction", + "preview": "[Ethers.js](https://docs.ethers.org/v6/){target=\\_blank} is a lightweight library that enables interaction with Ethereum Virtual Machine (EVM)-compatible blockchains through JavaScript. Ethers is widely used as a toolkit to establish connections and read and write blockchain data. This article demonstrates using Ethers.js to interact and deploy smart contracts to Polkadot Hub.", "outline": [ { "depth": 2, @@ -5003,11 +5003,6 @@ "title": "Compile Contracts", "anchor": "compile-contracts" }, - { - "depth": 3, - "title": "Install the Revive Library", - "anchor": "install-the-revive-library" - }, { "depth": 3, "title": "Sample Storage Smart Contract", @@ -5035,12 +5030,12 @@ } ], "stats": { - "chars": 20457, - "words": 2333, - "headings": 13, - "estimated_token_count_total": 4474 + "chars": 21292, + "words": 2412, + "headings": 12, + "estimated_token_count_total": 4742 }, - "hash": "sha256:c74a28d8d62369591c5734535136508db3d1f7380e486fd214f98d433cafd6e7", + "hash": "sha256:3493aaeec569be8d72f418c9b1abeeed64ff3bbcc316b42a33dad5f172c4e841", "token_estimator": "heuristic-v1" }, { diff --git a/.snippets/code/smart-contracts/libraries/ethers-js/checkStorage.js b/.snippets/code/smart-contracts/libraries/ethers-js/checkStorage.js index d95379916..8febb14e5 100644 --- a/.snippets/code/smart-contracts/libraries/ethers-js/checkStorage.js +++ b/.snippets/code/smart-contracts/libraries/ethers-js/checkStorage.js @@ -2,6 +2,8 @@ const { ethers } = require('ethers'); const { readFileSync } = require('fs'); const { join } = require('path'); +const artifactsDir = join(__dirname, '../contracts'); + const createProvider = (providerConfig) => { return new ethers.JsonRpcProvider(providerConfig.rpc, { chainId: providerConfig.chainId, @@ -13,7 +15,7 @@ const createWallet = (mnemonic, provider) => { return ethers.Wallet.fromPhrase(mnemonic).connect(provider); }; -const loadContractAbi = (contractName, directory = __dirname) => { +const loadContractAbi = (contractName, directory = artifactsDir) => { const contractPath = join(directory, `${contractName}.json`); const contractJson = JSON.parse(readFileSync(contractPath, 'utf8')); return contractJson.abi || contractJson; // Depending on JSON structure @@ -69,9 +71,9 @@ const providerConfig = { chainId: 420420422, }; -const mnemonic = 'INSERT_MNEMONIC'; +const mnemonic = 'INSERT_MNEMONIC' const contractName = 'Storage'; -const contractAddress = 'INSERT_CONTRACT_ADDRESS'; +const contractAddress = 'INSERT_CONTRACT_ADDRESS' const newNumber = 42; interactWithStorageContract( @@ -80,4 +82,4 @@ interactWithStorageContract( mnemonic, providerConfig, newNumber, -); +); \ No newline at end of file diff --git a/.snippets/code/smart-contracts/libraries/ethers-js/compile.js b/.snippets/code/smart-contracts/libraries/ethers-js/compile.js index a02829666..ec13f81ba 100644 --- a/.snippets/code/smart-contracts/libraries/ethers-js/compile.js +++ b/.snippets/code/smart-contracts/libraries/ethers-js/compile.js @@ -1,37 +1,71 @@ -const { compile } = require('@parity/resolc'); -const { readFileSync, writeFileSync } = require('fs'); +const solc = require('solc'); +const { readFileSync, writeFileSync, mkdirSync, existsSync } = require('fs'); const { basename, join } = require('path'); -const compileContract = async (solidityFilePath, outputDir) => { +const ensureDir = (dirPath) => { + if (!existsSync(dirPath)) { + mkdirSync(dirPath, { recursive: true }); + } +}; + +const compileContract = (solidityFilePath, abiDir, artifactsDir) => { try { // Read the Solidity file const source = readFileSync(solidityFilePath, 'utf8'); - - // Construct the input object for the compiler + const fileName = basename(solidityFilePath); + + // Construct the input object for the Solidity compiler const input = { - [basename(solidityFilePath)]: { content: source }, + language: 'Solidity', + sources: { + [fileName]: { + content: source, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['abi', 'evm.bytecode'], + }, + }, + }, }; - - console.log(`Compiling contract: ${basename(solidityFilePath)}...`); - + + console.log(`Compiling contract: ${fileName}...`); + // Compile the contract - const out = await compile(input); - - for (const contracts of Object.values(out.contracts)) { - for (const [name, contract] of Object.entries(contracts)) { - console.log(`Compiled contract: ${name}`); + const output = JSON.parse(solc.compile(JSON.stringify(input))); + + // Check for errors + if (output.errors) { + const errors = output.errors.filter(error => error.severity === 'error'); + if (errors.length > 0) { + console.error('Compilation errors:'); + errors.forEach(err => console.error(err.formattedMessage)); + return; + } + // Show warnings + const warnings = output.errors.filter(error => error.severity === 'warning'); + warnings.forEach(warn => console.warn(warn.formattedMessage)); + } + + // Ensure output directories exist + ensureDir(abiDir); + ensureDir(artifactsDir); + // Process compiled contracts + for (const [sourceFile, contracts] of Object.entries(output.contracts)) { + for (const [contractName, contract] of Object.entries(contracts)) { + console.log(`Compiled contract: ${contractName}`); + // Write the ABI - const abiPath = join(outputDir, `${name}.json`); + const abiPath = join(abiDir, `${contractName}.json`); writeFileSync(abiPath, JSON.stringify(contract.abi, null, 2)); console.log(`ABI saved to ${abiPath}`); - + // Write the bytecode - const bytecodePath = join(outputDir, `${name}.polkavm`); - writeFileSync( - bytecodePath, - Buffer.from(contract.evm.bytecode.object, 'hex'), - ); + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + writeFileSync(bytecodePath, contract.evm.bytecode.object); console.log(`Bytecode saved to ${bytecodePath}`); } } @@ -41,6 +75,7 @@ const compileContract = async (solidityFilePath, outputDir) => { }; const solidityFilePath = join(__dirname, '../contracts/Storage.sol'); -const outputDir = join(__dirname, '../contracts'); +const abiDir = join(__dirname, '../abis'); +const artifactsDir = join(__dirname, '../artifacts'); -compileContract(solidityFilePath, outputDir); +compileContract(solidityFilePath, abiDir, artifactsDir); \ No newline at end of file diff --git a/.snippets/code/smart-contracts/libraries/ethers-js/deploy.js b/.snippets/code/smart-contracts/libraries/ethers-js/deploy.js index 40c78e463..c753c714a 100644 --- a/.snippets/code/smart-contracts/libraries/ethers-js/deploy.js +++ b/.snippets/code/smart-contracts/libraries/ethers-js/deploy.js @@ -1,11 +1,11 @@ -// Deploy an EVM-compatible smart contract using ethers.js const { writeFileSync, existsSync, readFileSync } = require('fs'); const { join } = require('path'); const { ethers, JsonRpcProvider } = require('ethers'); -const codegenDir = join(__dirname); +const scriptsDir = __dirname; +const artifactsDir = join(__dirname, '../contracts'); -// Creates an Ethereum provider with specified RPC URL and chain details +// Creates a provider with specified RPC URL and chain details const createProvider = (rpcUrl, chainId, chainName) => { const provider = new JsonRpcProvider(rpcUrl, { chainId: chainId, @@ -17,9 +17,8 @@ const createProvider = (rpcUrl, chainId, chainName) => { // Reads and parses the ABI file for a given contract const getAbi = (contractName) => { try { - return JSON.parse( - readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'), - ); + const abiPath = join(artifactsDir, `${contractName}.json`); + return JSON.parse(readFileSync(abiPath, 'utf8')); } catch (error) { console.error( `Could not find ABI for contract ${contractName}:`, @@ -32,12 +31,10 @@ const getAbi = (contractName) => { // Reads the compiled bytecode for a given contract const getByteCode = (contractName) => { try { - const bytecodePath = join( - codegenDir, - '../contracts', - `${contractName}.polkavm`, - ); - return `0x${readFileSync(bytecodePath).toString('hex')}`; + const bytecodePath = join(artifactsDir, `${contractName}.bin`); + const bytecode = readFileSync(bytecodePath, 'utf8').trim(); + // Add 0x prefix if not present + return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; } catch (error) { console.error( `Could not find bytecode for contract ${contractName}:`, @@ -49,7 +46,6 @@ const getByteCode = (contractName) => { const deployContract = async (contractName, mnemonic, providerConfig) => { console.log(`Deploying ${contractName}...`); - try { // Step 1: Set up provider and wallet const provider = createProvider( @@ -73,10 +69,11 @@ const deployContract = async (contractName, mnemonic, providerConfig) => { const address = await contract.getAddress(); console.log(`Contract ${contractName} deployed at: ${address}`); - const addressesFile = join(codegenDir, 'contract-address.json'); + const addressesFile = join(scriptsDir, 'contract-address.json'); const addresses = existsSync(addressesFile) ? JSON.parse(readFileSync(addressesFile, 'utf8')) : {}; + addresses[contractName] = address; writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8'); } catch (error) { @@ -85,11 +82,11 @@ const deployContract = async (contractName, mnemonic, providerConfig) => { }; const providerConfig = { - rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', + rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready chainId: 420420422, name: 'polkadot-hub-testnet', }; const mnemonic = 'INSERT_MNEMONIC'; -deployContract('Storage', mnemonic, providerConfig); +deployContract('Storage', mnemonic, providerConfig); \ No newline at end of file diff --git a/code/smart-contracts/libraries/ethers-js/Storage.sol b/code/smart-contracts/libraries/ethers-js/Storage.sol new file mode 100644 index 000000000..a306bb393 --- /dev/null +++ b/code/smart-contracts/libraries/ethers-js/Storage.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +contract Storage { + uint256 private storedNumber; + + event NumberUpdated(uint256 newValue); + + function store(uint256 num) public { + storedNumber = num; + emit NumberUpdated(num); + } + + function retrieve() public view returns (uint256) { + return storedNumber; + } +} diff --git a/llms-full.jsonl b/llms-full.jsonl index 4c48cef28..32d2bd16f 100644 --- a/llms-full.jsonl +++ b/llms-full.jsonl @@ -647,25 +647,24 @@ {"page_id": "smart-contracts-get-started", "page_title": "Get Started with Smart Contracts", "index": 4, "depth": 2, "title": "Libraries", "anchor": "libraries", "start_char": 7141, "end_char": 8154, "estimated_token_count": 317, "token_estimator": "heuristic-v1", "text": "## Libraries\n\nChoose the client libraries that fit your stack for connecting wallets and calling contracts.\n\n| Library | Description |\n|:------------------------------------------------------------------:|:-------------------------------------------------------:|\n| [Ethers.js](/smart-contracts/libraries/ethers-js/){target=\\_blank} | Connect, sign, and interact with contracts using Ethers |\n| [viem](/smart-contracts/libraries/viem/){target=\\_blank} | Type‑safe EVM interactions and utilities |\n| [Wagmi](/smart-contracts/libraries/wagmi/){target=\\_blank} | React hooks for wallet connections and contract calls |\n| [Web3.js](/smart-contracts/libraries/web3-js/){target=\\_blank} | Web3 provider and contract APIs |\n| [Web3.py](/smart-contracts/libraries/web3-py/){target=\\_blank} | Python toolkit for on‑chain interactions and scripts |"} {"page_id": "smart-contracts-get-started", "page_title": "Get Started with Smart Contracts", "index": 5, "depth": 2, "title": "Integrations", "anchor": "integrations", "start_char": 8154, "end_char": 8842, "estimated_token_count": 224, "token_estimator": "heuristic-v1", "text": "## Integrations\n\nIntegrate essential services like wallets, indexers, and oracles to round out your dApp.\n\n| Integration | Description |\n|:-------------------------------------------------------------------:|:-----------------------------------------:|\n| [Wallets](/smart-contracts/integrations/wallets/){target=\\_blank} | Supported wallets and configuration notes |\n| [Indexers](/smart-contracts/integrations/indexers/){target=\\_blank} | Index and query blockchain data |\n| [Oracles](/smart-contracts/integrations/oracles/){target=\\_blank} | Bring external data on‑chain |"} {"page_id": "smart-contracts-get-started", "page_title": "Get Started with Smart Contracts", "index": 6, "depth": 2, "title": "Precompiles", "anchor": "precompiles", "start_char": 8842, "end_char": 10103, "estimated_token_count": 362, "token_estimator": "heuristic-v1", "text": "## Precompiles\n\nDiscover precompiled system contracts available on the Hub and how to use them.\n\n| Topic | Description |\n|:------------------------------------------------------------------------:|:---------------------------------------------------:|\n| [Overview of Precompiles](/smart-contracts/precompiles/){target=\\_blank} | What precompiles are available on the Hub |\n| [ETH Native](/smart-contracts/precompiles/eth-native/){target=\\_blank} | EVM precompiles and interfaces |\n| [Staking](/smart-contracts/precompiles/staking/){target=\\_blank} | Interact with staking functionality via precompiles |\n| [XCM](/smart-contracts/precompiles/xcm/){target=\\_blank} | Cross‑chain messaging helpers for contracts |\n\nFrom here, follow the quick starts to get connected, iterate locally with your preferred tools, and use the guides, libraries, integrations, and precompiles as you grow into production‑ready dApps. If you get stuck, [open an issue](https://github.com/polkadot-developers/polkadot-docs/issues/new?template=docs-issue.yml){target=\\_blank} or reach out in the community channels."} -{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 0, "depth": 2, "title": "Introduction", "anchor": "introduction", "start_char": 28, "end_char": 495, "estimated_token_count": 75, "token_estimator": "heuristic-v1", "text": "## Introduction\n\nConnecting a compatible wallet is the first essential step for interacting with the Polkadot Hub ecosystem. This guide explores wallet options that support both Substrate and Ethereum compatible layers, enabling transactions and smart contract interactions. Whether you're a developer testing on Polkadot Hub or a user accessing the MainNet, understanding wallet configuration is crucial for accessing the full range of Polkadot Hub's capabilities."} -{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 1, "depth": 2, "title": "Connect Your Wallet", "anchor": "connect-your-wallet", "start_char": 495, "end_char": 519, "estimated_token_count": 5, "token_estimator": "heuristic-v1", "text": "## Connect Your Wallet"} -{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 2, "depth": 3, "title": "MetaMask", "anchor": "metamask", "start_char": 519, "end_char": 2213, "estimated_token_count": 403, "token_estimator": "heuristic-v1", "text": "### MetaMask\n\n[MetaMask](https://metamask.io/){target=\\_blank} is a popular wallet for interacting with Ethereum-compatible chains. It allows users to connect to test networks that support Ethereum-based smart contracts. However, it's important to emphasize that MetaMask primarily facilitates interactions with smart contracts, giving users access to various chain functionalities. \n\nTo get started with MetaMask, you need to install the [MetaMask extension](https://metamask.io/download/){target=\\_blank} and add it to the browser. Once you install MetaMask, you can set up a new wallet and securely store your seed phrase. This phrase is crucial for recovery in case you lose access.\n\nFor example, to connect to the Polkadot Hub TestNet via MetaMask, you need to follow these steps:\n\n1. Open the MetaMask extension and click on the network icon to switch to the Polkadot Hub TestNet.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-1.webp){: .browser-extension}\n\n2. Click on the **Add a custom network** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-2.webp){: .browser-extension}\n\n3. Complete the necessary fields, then click the **Save** button (refer to the [Networks](/smart-contracts/connect/#networks-details){target=\\_blank} section for copy and paste parameters).\n\n ![](/images/smart-contracts/integrations/wallets/wallets-3.webp){: .browser-extension}\n\n4. Click on **Polkadot Hub TestNet** to switch the network.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-4.webp){: .browser-extension}\n\nThe steps in the preceding section can be used to connect to any chain by modifying the network specification and endpoint parameters."} -{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 3, "depth": 3, "title": "SubWallet", "anchor": "subwallet", "start_char": 2213, "end_char": 4799, "estimated_token_count": 613, "token_estimator": "heuristic-v1", "text": "### SubWallet\n\n[SubWallet](https://www.subwallet.app/){target=\\_blank} is a popular non-custodial wallet solution for Polkadot and Ethereum ecosystems. It offers seamless integration with Polkadot SDK-based networks while maintaining Ethereum compatibility, making the wallet an ideal choice for users and developers to interact with Polkadot Hub.\n\nSubWallet now fully supports the [Polkadot Hub TestNet](/polkadot-protocol/smart-contract-basics/networks/#test-networks){target=\\_blank} where developers can deploy and interact with Ethereum-compatible, Solidity smart contracts.\n\nYou can easily view and manage your Paseo native token (PAS) using the Ethereum RPC endpoint (Passet Hub EVM) or the Substrate node RPC endpoint (passet-hub).\n\n??? code \"Polkadot Hub TestNet\"\n You can see support here for Polkadot Hub's TestNet. The **Passet Hub EVM** network uses an ETH RPC endpoint, and the **passet-hub** uses a Substrate endpoint.\n The ETH RPC endpoint will let you send transactions that follow an ETH format, while the Substrate endpoint will follow a Substrate transaction format.\n Note the PAS token, which is the native token of the Polkadot Hub TestNet.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-PAS.webp){: .browser-extension}\n\nTo connect to Polkadot Hub TestNet using SubWallet, follow these steps:\n\n1. Install the [SubWallet browser extension](https://chromewebstore.google.com/detail/subwallet-polkadot-wallet/onhogfjeacnfoofkfgppdlbmlmnplgbn?hl=en){target=\\_blank} and set up your wallet by following the on-screen instructions, or refer to our [step-by-step guide](https://docs.subwallet.app/main/extension-user-guide/getting-started/install-subwallet){target=\\_blank} for assistance.\n\n2. After setting up your wallet, click the List icon at the top left corner of the extension window to open **Settings**.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-01.webp){: .browser-extension}\n\n3. Scroll down and select **Manage networks**.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-02.webp){: .browser-extension}\n\n4. In the Manage network screen, either scroll down or type in the search bar to find the networks. Once done, enable the toggle next to the network name.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-03.webp){: .browser-extension}\n\n You are now ready to use SubWallet to interact with [Polkadot Hub TestNet](/smart-contracts/connect/#networks-details){target=\\_blank} seamlessly!\n\n![](/images/smart-contracts/integrations/wallets/subwallet-04.webp){: .browser-extension}"} -{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 4, "depth": 3, "title": "Talisman", "anchor": "talisman", "start_char": 4799, "end_char": 6546, "estimated_token_count": 431, "token_estimator": "heuristic-v1", "text": "### Talisman\n\n[Talisman](https://talisman.xyz/){target=\\_blank} is a specialized wallet for the Polkadot ecosystem that supports both Substrate and EVM accounts, making it an excellent choice for Polkadot Hub interactions. Talisman offers a more integrated experience for Polkadot-based chains while still providing Ethereum compatibility.\n\nTo use Talisman with Polkadot Hub TestNet:\n\n1. Install the [Talisman extension](https://talisman.xyz/download){target=\\_blank} and set up your wallet by following the on-screen instructions.\n\n2. Once installed, click on the Talisman icon in your browser extensions and click on the **Settings** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-5.webp){: .browser-extension}\n\n3. Click the button **All settings**.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-6.webp){: .browser-extension}\n\n4. Go to the **Networks & Tokens** section.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-7.webp)\n\n5. Click the **Manage networks** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-8.webp)\n\n6. Click the **+ Add network** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-9.webp)\n\n7. Fill in the form with the required parameters and click the **Add network** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-10.webp)\n\n8. After that, you can switch to the Polkadot Hub TestNet by clicking on the network icon and selecting **Polkadot Hub TestNet**.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-11.webp)\n\nAfter selecting the network, Talisman will automatically configure the necessary RPC URL and chain ID for you. You can now use Talisman to interact with the Polkadot Hub TestNet."} -{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 5, "depth": 2, "title": "Conclusion", "anchor": "conclusion", "start_char": 6546, "end_char": 7170, "estimated_token_count": 100, "token_estimator": "heuristic-v1", "text": "## Conclusion\n\nChoosing the right wallet for Polkadot Hub interactions depends on your specific requirements and familiarity with different interfaces. MetaMask provides a familiar entry point for developers with Ethereum experience, while Talisman offers deeper integration with Polkadot's unique features and native support for both EVM and Substrate accounts. By properly configuring your wallet connection, you gain access to the full spectrum of Polkadot Hub's capabilities.\n\n!!!info\n Remember to always verify network parameters when connecting to ensure a secure and reliable connection to the Polkadot ecosystem."} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 0, "depth": 2, "title": "Introduction", "anchor": "introduction", "start_char": 183, "end_char": 709, "estimated_token_count": 105, "token_estimator": "heuristic-v1", "text": "## Introduction\n\n[Ethers.js](https://docs.ethers.org/v6/){target=\\_blank} is a lightweight library that enables interaction with Ethereum Virtual Machine (EVM)-compatible blockchains through JavaScript. Ethers is widely used as a toolkit to establish connections and read and write blockchain data. This article demonstrates using Ethers.js to interact and deploy smart contracts to Polkadot Hub.\n\nThis guide is intended for developers who are familiar with JavaScript and want to interact with Polkadot Hub using Ethers.js."} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 1, "depth": 2, "title": "Prerequisites", "anchor": "prerequisites", "start_char": 709, "end_char": 1065, "estimated_token_count": 110, "token_estimator": "heuristic-v1", "text": "## Prerequisites\n\nBefore getting started, ensure you have the following installed:\n\n- **Node.js**: v22.13.1 or later, check the [Node.js installation guide](https://nodejs.org/en/download/current/){target=\\_blank}.\n- **npm**: v6.13.4 or later (comes bundled with Node.js).\n- **Solidity**: This guide uses Solidity `^0.8.9` for smart contract development."} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 2, "depth": 2, "title": "Project Structure", "anchor": "project-structure", "start_char": 1065, "end_char": 1577, "estimated_token_count": 144, "token_estimator": "heuristic-v1", "text": "## Project Structure\n\nThis project organizes contracts, scripts, and compiled artifacts for easy development and deployment.\n\n```text title=\"Ethers.js Polkadot Hub\"\nethers-project\n├── contracts\n│ ├── Storage.sol\n├── scripts\n│ ├── connectToProvider.js\n│ ├── fetchLastBlock.js\n│ ├── compile.js\n│ ├── deploy.js\n│ ├── checkStorage.js\n├── abis\n│ ├── Storage.json\n├── artifacts\n│ ├── Storage.polkavm\n├── contract-address.json\n├── node_modules/\n├── package.json\n├── package-lock.json\n└── README.md\n```"} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 3, "depth": 2, "title": "Set Up the Project", "anchor": "set-up-the-project", "start_char": 1577, "end_char": 1798, "estimated_token_count": 50, "token_estimator": "heuristic-v1", "text": "## Set Up the Project\n\nTo start working with Ethers.js, create a new folder and initialize your project by running the following commands in your terminal:\n\n```bash\nmkdir ethers-project\ncd ethers-project\nnpm init -y\n```"} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 4, "depth": 2, "title": "Install Dependencies", "anchor": "install-dependencies", "start_char": 1798, "end_char": 1922, "estimated_token_count": 28, "token_estimator": "heuristic-v1", "text": "## Install Dependencies\n\nNext, run the following command to install the Ethers.js library:\n\n```bash\nnpm install ethers\n```"} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 5, "depth": 2, "title": "Set Up the Ethers.js Provider", "anchor": "set-up-the-ethersjs-provider", "start_char": 1922, "end_char": 4510, "estimated_token_count": 574, "token_estimator": "heuristic-v1", "text": "## Set Up the Ethers.js Provider\n\nA [`Provider`](https://docs.ethers.org/v6/api/providers/#Provider){target=\\_blank} is an abstraction of a connection to the Ethereum network, allowing you to query blockchain data and send transactions. It serves as a bridge between your application and the blockchain.\n\nTo interact with Polkadot Hub, you must set up an Ethers.js provider. This provider connects to a blockchain node, allowing you to query blockchain data and interact with smart contracts. In the root of your project, create a file named `connectToProvider.js` and add the following code:\n\n```js title=\"scripts/connectToProvider.js\"\nconst { JsonRpcProvider } = require('ethers');\n\nconst createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n\n return provider;\n};\n\nconst PROVIDER_RPC = {\n rpc: 'INSERT_RPC_URL',\n chainId: 'INSERT_CHAIN_ID',\n name: 'INSERT_CHAIN_NAME',\n};\n\ncreateProvider(PROVIDER_RPC.rpc, PROVIDER_RPC.chainId, PROVIDER_RPC.name);\n\n```\n\n!!! note\n Replace `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, and `INSERT_CHAIN_NAME` with the appropriate values. For example, to connect to Polkadot Hub TestNet's Ethereum RPC instance, you can use the following parameters:\n\n ```js\n const PROVIDER_RPC = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n name: 'polkadot-hub-testnet'\n };\n ```\n\nTo connect to the provider, execute:\n\n```bash\nnode connectToProvider\n```\n\nWith the provider set up, you can start querying the blockchain. For instance, to fetch the latest block number:\n\n??? code \"Fetch Last Block code\"\n\n ```js title=\"scripts/fetchLastBlock.js\"\n const { JsonRpcProvider } = require('ethers');\n\n const createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n\n return provider;\n };\n\n const PROVIDER_RPC = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n name: 'polkadot-hub-testnet',\n };\n\n const main = async () => {\n try {\n const provider = createProvider(\n PROVIDER_RPC.rpc,\n PROVIDER_RPC.chainId,\n PROVIDER_RPC.name,\n );\n const latestBlock = await provider.getBlockNumber();\n console.log(`Latest block: ${latestBlock}`);\n } catch (error) {\n console.error('Error connecting to Polkadot Hub TestNet: ' + error.message);\n }\n };\n\n main();\n\n ```"} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 6, "depth": 2, "title": "Compile Contracts", "anchor": "compile-contracts", "start_char": 4510, "end_char": 5230, "estimated_token_count": 154, "token_estimator": "heuristic-v1", "text": "## Compile Contracts\n\n!!! note \"Contracts Code Blob Size Disclaimer\"\n The maximum contract code blob size on Polkadot Hub networks is _100 kilobytes_, significantly larger than Ethereum’s EVM limit of 24 kilobytes.\n\n For detailed comparisons and migration guidelines, see the [EVM vs. PolkaVM](/polkadot-protocol/smart-contract-basics/evm-vs-polkavm/#current-memory-limits){target=\\_blank} documentation page.\n\nThe `revive` compiler transforms Solidity smart contracts into [PolkaVM](/smart-contracts/overview/#native-smart-contracts){target=\\_blank} bytecode for deployment on Polkadot Hub. Revive's Ethereum RPC interface allows you to use familiar tools like Ethers.js and MetaMask to interact with contracts."} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 7, "depth": 3, "title": "Install the Revive Library", "anchor": "install-the-revive-library", "start_char": 5230, "end_char": 5590, "estimated_token_count": 100, "token_estimator": "heuristic-v1", "text": "### Install the Revive Library\n\nThe [`@parity/resolc`](https://www.npmjs.com/package/@parity/resolc){target=\\_blank} library will compile your Solidity code for deployment on Polkadot Hub. Run the following command in your terminal to install the library:\n\n```bash\nnpm install --save-dev @parity/resolc \n```\n\nThis guide uses `@parity/resolc` version `0.2.0`."} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 8, "depth": 3, "title": "Sample Storage Smart Contract", "anchor": "sample-storage-smart-contract", "start_char": 5590, "end_char": 6415, "estimated_token_count": 171, "token_estimator": "heuristic-v1", "text": "### Sample Storage Smart Contract\n\nThis example demonstrates compiling a `Storage.sol` Solidity contract for deployment to Polkadot Hub. The contract's functionality stores a number and permits users to update it with a new value.\n\n```solidity title=\"contracts/Storage.sol\"\n//SPDX-License-Identifier: MIT\n\n// Solidity files have to start with this pragma.\n// It will be used by the Solidity compiler to validate its version.\npragma solidity ^0.8.9;\n\ncontract Storage {\n // Public state variable to store a number\n uint256 public storedNumber;\n\n /**\n * Updates the stored number.\n *\n * The `public` modifier allows anyone to call this function.\n *\n * @param _newNumber - The new value to store.\n */\n function setNumber(uint256 _newNumber) public {\n storedNumber = _newNumber;\n }\n}\n```"} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 9, "depth": 3, "title": "Compile the Smart Contract", "anchor": "compile-the-smart-contract", "start_char": 6415, "end_char": 8904, "estimated_token_count": 567, "token_estimator": "heuristic-v1", "text": "### Compile the Smart Contract\n\nTo compile this contract, use the following script:\n\n```js title=\"scripts/compile.js\"\nconst { compile } = require('@parity/resolc');\nconst { readFileSync, writeFileSync } = require('fs');\nconst { basename, join } = require('path');\n\nconst compileContract = async (solidityFilePath, outputDir) => {\n try {\n // Read the Solidity file\n const source = readFileSync(solidityFilePath, 'utf8');\n\n // Construct the input object for the compiler\n const input = {\n [basename(solidityFilePath)]: { content: source },\n };\n\n console.log(`Compiling contract: ${basename(solidityFilePath)}...`);\n\n // Compile the contract\n const out = await compile(input);\n\n for (const contracts of Object.values(out.contracts)) {\n for (const [name, contract] of Object.entries(contracts)) {\n console.log(`Compiled contract: ${name}`);\n\n // Write the ABI\n const abiPath = join(outputDir, `${name}.json`);\n writeFileSync(abiPath, JSON.stringify(contract.abi, null, 2));\n console.log(`ABI saved to ${abiPath}`);\n\n // Write the bytecode\n const bytecodePath = join(outputDir, `${name}.polkavm`);\n writeFileSync(\n bytecodePath,\n Buffer.from(contract.evm.bytecode.object, 'hex'),\n );\n console.log(`Bytecode saved to ${bytecodePath}`);\n }\n }\n } catch (error) {\n console.error('Error compiling contracts:', error);\n }\n};\n\nconst solidityFilePath = join(__dirname, '../contracts/Storage.sol');\nconst outputDir = join(__dirname, '../contracts');\n\ncompileContract(solidityFilePath, outputDir);\n\n```\n\n!!! note \n The script above is tailored to the `Storage.sol` contract. It can be adjusted for other contracts by changing the file name or modifying the ABI and bytecode paths.\n\nThe ABI (Application Binary Interface) is a JSON representation of your contract's functions, events, and their parameters. It serves as the interface between your JavaScript code and the deployed smart contract, allowing your application to know how to format function calls and interpret returned data.\n\nExecute the script above by running:\n\n```bash\nnode compile\n```\n\nAfter executing the script, the Solidity contract will be compiled into the required PolkaVM bytecode format. The ABI and bytecode will be saved into files with `.json` and `.polkavm` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section."} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 10, "depth": 2, "title": "Deploy the Compiled Contract", "anchor": "deploy-the-compiled-contract", "start_char": 8904, "end_char": 16591, "estimated_token_count": 1604, "token_estimator": "heuristic-v1", "text": "## Deploy the Compiled Contract\n\nTo deploy your compiled contract to Polkadot Hub, you'll need a wallet with a private key to sign the deployment transaction.\n\nYou can create a `deploy.js` script in the root of your project to achieve this. The deployment script can be divided into key components:\n\n1. Set up the required imports and utilities:\n\n ```js title=\"scripts/deploy.js\"\n // Deploy an EVM-compatible smart contract using ethers.js\n const { writeFileSync, existsSync, readFileSync } = require('fs');\n const { join } = require('path');\n const { ethers, JsonRpcProvider } = require('ethers');\n\n const codegenDir = join(__dirname);\n ```\n\n2. Create a provider to connect to Polkadot Hub:\n\n ```js title=\"scripts/deploy.js\"\n\n // Creates an Ethereum provider with specified RPC URL and chain details\n const createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n return provider;\n };\n ```\n \n3. Set up functions to read contract artifacts:\n\n ```js title=\"scripts/deploy.js\"\n // Reads and parses the ABI file for a given contract\n const getAbi = (contractName) => {\n try {\n return JSON.parse(\n readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'),\n );\n } catch (error) {\n console.error(\n `Could not find ABI for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n\n // Reads the compiled bytecode for a given contract\n const getByteCode = (contractName) => {\n try {\n const bytecodePath = join(\n codegenDir,\n '../contracts',\n `${contractName}.polkavm`,\n );\n return `0x${readFileSync(bytecodePath).toString('hex')}`;\n } catch (error) {\n console.error(\n `Could not find bytecode for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n ```\n\n4. Create the main deployment function:\n\n ```js title=\"scripts/deploy.js\"\n\n const deployContract = async (contractName, mnemonic, providerConfig) => {\n console.log(`Deploying ${contractName}...`);\n\n try {\n // Step 1: Set up provider and wallet\n const provider = createProvider(\n providerConfig.rpc,\n providerConfig.chainId,\n providerConfig.name,\n );\n const walletMnemonic = ethers.Wallet.fromPhrase(mnemonic);\n const wallet = walletMnemonic.connect(provider);\n\n // Step 2: Create and deploy the contract\n const factory = new ethers.ContractFactory(\n getAbi(contractName),\n getByteCode(contractName),\n wallet,\n );\n const contract = await factory.deploy();\n await contract.waitForDeployment();\n\n // Step 3: Save deployment information\n const address = await contract.getAddress();\n console.log(`Contract ${contractName} deployed at: ${address}`);\n\n const addressesFile = join(codegenDir, 'contract-address.json');\n const addresses = existsSync(addressesFile)\n ? JSON.parse(readFileSync(addressesFile, 'utf8'))\n : {};\n addresses[contractName] = address;\n writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8');\n } catch (error) {\n console.error(`Failed to deploy contract ${contractName}:`, error);\n }\n };\n ```\n\n5. Configure and execute the deployment:\n\n ```js title=\"scripts/deploy.js\"\n const providerConfig = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n name: 'polkadot-hub-testnet',\n };\n\n const mnemonic = 'INSERT_MNEMONIC';\n\n deployContract('Storage', mnemonic, providerConfig);\n ```\n\n !!! note\n A mnemonic (seed phrase) is a series of words that can generate multiple private keys and their corresponding addresses. It's used here to derive the wallet that will sign and pay for the deployment transaction. **Always keep your mnemonic secure and never share it publicly**.\n\n Ensure to replace the `INSERT_MNEMONIC` placeholder with your actual mnemonic.\n\n??? code \"View complete script\"\n\n ```js title=\"scripts/deploy.js\"\n // Deploy an EVM-compatible smart contract using ethers.js\n const { writeFileSync, existsSync, readFileSync } = require('fs');\n const { join } = require('path');\n const { ethers, JsonRpcProvider } = require('ethers');\n\n const codegenDir = join(__dirname);\n\n // Creates an Ethereum provider with specified RPC URL and chain details\n const createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n return provider;\n };\n\n // Reads and parses the ABI file for a given contract\n const getAbi = (contractName) => {\n try {\n return JSON.parse(\n readFileSync(join(codegenDir, `${contractName}.json`), 'utf8'),\n );\n } catch (error) {\n console.error(\n `Could not find ABI for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n\n // Reads the compiled bytecode for a given contract\n const getByteCode = (contractName) => {\n try {\n const bytecodePath = join(\n codegenDir,\n '../contracts',\n `${contractName}.polkavm`,\n );\n return `0x${readFileSync(bytecodePath).toString('hex')}`;\n } catch (error) {\n console.error(\n `Could not find bytecode for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n\n const deployContract = async (contractName, mnemonic, providerConfig) => {\n console.log(`Deploying ${contractName}...`);\n\n try {\n // Step 1: Set up provider and wallet\n const provider = createProvider(\n providerConfig.rpc,\n providerConfig.chainId,\n providerConfig.name,\n );\n const walletMnemonic = ethers.Wallet.fromPhrase(mnemonic);\n const wallet = walletMnemonic.connect(provider);\n\n // Step 2: Create and deploy the contract\n const factory = new ethers.ContractFactory(\n getAbi(contractName),\n getByteCode(contractName),\n wallet,\n );\n const contract = await factory.deploy();\n await contract.waitForDeployment();\n\n // Step 3: Save deployment information\n const address = await contract.getAddress();\n console.log(`Contract ${contractName} deployed at: ${address}`);\n\n const addressesFile = join(codegenDir, 'contract-address.json');\n const addresses = existsSync(addressesFile)\n ? JSON.parse(readFileSync(addressesFile, 'utf8'))\n : {};\n addresses[contractName] = address;\n writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8');\n } catch (error) {\n console.error(`Failed to deploy contract ${contractName}:`, error);\n }\n };\n\n const providerConfig = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n name: 'polkadot-hub-testnet',\n };\n\n const mnemonic = 'INSERT_MNEMONIC';\n\n deployContract('Storage', mnemonic, providerConfig);\n\n ```\n\nTo run the script, execute the following command:\n\n```bash\nnode deploy\n```\n\nAfter running this script, your contract will be deployed to Polkadot Hub, and its address will be saved in `contract-address.json` within your project directory. You can use this address for future contract interactions."} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 11, "depth": 2, "title": "Interact with the Contract", "anchor": "interact-with-the-contract", "start_char": 16591, "end_char": 19852, "estimated_token_count": 712, "token_estimator": "heuristic-v1", "text": "## Interact with the Contract\n\nOnce the contract is deployed, you can interact with it by calling its functions. For example, to set a number, read it and then modify that number by its double, you can create a file named `checkStorage.js` in the root of your project and add the following code:\n\n```js title=\"scripts/checkStorage.js\"\nconst { ethers } = require('ethers');\nconst { readFileSync } = require('fs');\nconst { join } = require('path');\n\nconst createProvider = (providerConfig) => {\n return new ethers.JsonRpcProvider(providerConfig.rpc, {\n chainId: providerConfig.chainId,\n name: providerConfig.name,\n });\n};\n\nconst createWallet = (mnemonic, provider) => {\n return ethers.Wallet.fromPhrase(mnemonic).connect(provider);\n};\n\nconst loadContractAbi = (contractName, directory = __dirname) => {\n const contractPath = join(directory, `${contractName}.json`);\n const contractJson = JSON.parse(readFileSync(contractPath, 'utf8'));\n return contractJson.abi || contractJson; // Depending on JSON structure\n};\n\nconst createContract = (contractAddress, abi, wallet) => {\n return new ethers.Contract(contractAddress, abi, wallet);\n};\n\nconst interactWithStorageContract = async (\n contractName,\n contractAddress,\n mnemonic,\n providerConfig,\n numberToSet,\n) => {\n try {\n console.log(`Setting new number in Storage contract: ${numberToSet}`);\n\n // Create provider and wallet\n const provider = createProvider(providerConfig);\n const wallet = createWallet(mnemonic, provider);\n\n // Load the contract ABI and create the contract instance\n const abi = loadContractAbi(contractName);\n const contract = createContract(contractAddress, abi, wallet);\n\n // Send a transaction to set the stored number\n const tx1 = await contract.setNumber(numberToSet);\n await tx1.wait(); // Wait for the transaction to be mined\n console.log(`Number successfully set to ${numberToSet}`);\n\n // Retrieve the updated number\n const storedNumber = await contract.storedNumber();\n console.log(`Retrieved stored number:`, storedNumber.toString());\n\n // Send a transaction to set the stored number\n const tx2 = await contract.setNumber(numberToSet * 2);\n await tx2.wait(); // Wait for the transaction to be mined\n console.log(`Number successfully set to ${numberToSet * 2}`);\n\n // Retrieve the updated number\n const updatedNumber = await contract.storedNumber();\n console.log(`Retrieved stored number:`, updatedNumber.toString());\n } catch (error) {\n console.error('Error interacting with Storage contract:', error.message);\n }\n};\n\nconst providerConfig = {\n name: 'asset-hub-smart-contracts',\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n};\n\nconst mnemonic = 'INSERT_MNEMONIC';\nconst contractName = 'Storage';\nconst contractAddress = 'INSERT_CONTRACT_ADDRESS';\nconst newNumber = 42;\n\ninteractWithStorageContract(\n contractName,\n contractAddress,\n mnemonic,\n providerConfig,\n newNumber,\n);\n\n```\n\nEnsure you replace the `INSERT_MNEMONIC`, `INSERT_CONTRACT_ADDRESS`, and `INSERT_ADDRESS_TO_CHECK` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced.\n\nTo interact with the contract, run:\n\n```bash\nnode checkStorage\n```"} -{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 12, "depth": 2, "title": "Where to Go Next", "anchor": "where-to-go-next", "start_char": 19852, "end_char": 20457, "estimated_token_count": 155, "token_estimator": "heuristic-v1", "text": "## Where to Go Next\n\nNow that you have the foundational knowledge to use Ethers.js with Polkadot Hub, you can:\n\n- **Dive into Ethers.js utilities**: Discover additional Ethers.js features, such as wallet management, signing messages, etc.\n- **Implement batch transactions**: Use Ethers.js to execute batch transactions for efficient multi-step contract interactions.\n- **Build scalable applications**: Combine Ethers.js with frameworks like [`Next.js`](https://nextjs.org/docs){target=\\_blank} or [`Node.js`](https://nodejs.org/en){target=\\_blank} to create full-stack decentralized applications (dApps)."} +{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 0, "depth": 2, "title": "Introduction", "anchor": "introduction", "start_char": 198, "end_char": 665, "estimated_token_count": 75, "token_estimator": "heuristic-v1", "text": "## Introduction\n\nConnecting a compatible wallet is the first essential step for interacting with the Polkadot Hub ecosystem. This guide explores wallet options that support both Substrate and Ethereum compatible layers, enabling transactions and smart contract interactions. Whether you're a developer testing on Polkadot Hub or a user accessing the MainNet, understanding wallet configuration is crucial for accessing the full range of Polkadot Hub's capabilities."} +{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 1, "depth": 2, "title": "Connect Your Wallet", "anchor": "connect-your-wallet", "start_char": 665, "end_char": 689, "estimated_token_count": 5, "token_estimator": "heuristic-v1", "text": "## Connect Your Wallet"} +{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 2, "depth": 3, "title": "MetaMask", "anchor": "metamask", "start_char": 689, "end_char": 2383, "estimated_token_count": 403, "token_estimator": "heuristic-v1", "text": "### MetaMask\n\n[MetaMask](https://metamask.io/){target=\\_blank} is a popular wallet for interacting with Ethereum-compatible chains. It allows users to connect to test networks that support Ethereum-based smart contracts. However, it's important to emphasize that MetaMask primarily facilitates interactions with smart contracts, giving users access to various chain functionalities. \n\nTo get started with MetaMask, you need to install the [MetaMask extension](https://metamask.io/download/){target=\\_blank} and add it to the browser. Once you install MetaMask, you can set up a new wallet and securely store your seed phrase. This phrase is crucial for recovery in case you lose access.\n\nFor example, to connect to the Polkadot Hub TestNet via MetaMask, you need to follow these steps:\n\n1. Open the MetaMask extension and click on the network icon to switch to the Polkadot Hub TestNet.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-1.webp){: .browser-extension}\n\n2. Click on the **Add a custom network** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-2.webp){: .browser-extension}\n\n3. Complete the necessary fields, then click the **Save** button (refer to the [Networks](/smart-contracts/connect/#networks-details){target=\\_blank} section for copy and paste parameters).\n\n ![](/images/smart-contracts/integrations/wallets/wallets-3.webp){: .browser-extension}\n\n4. Click on **Polkadot Hub TestNet** to switch the network.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-4.webp){: .browser-extension}\n\nThe steps in the preceding section can be used to connect to any chain by modifying the network specification and endpoint parameters."} +{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 3, "depth": 3, "title": "SubWallet", "anchor": "subwallet", "start_char": 2383, "end_char": 4969, "estimated_token_count": 613, "token_estimator": "heuristic-v1", "text": "### SubWallet\n\n[SubWallet](https://www.subwallet.app/){target=\\_blank} is a popular non-custodial wallet solution for Polkadot and Ethereum ecosystems. It offers seamless integration with Polkadot SDK-based networks while maintaining Ethereum compatibility, making the wallet an ideal choice for users and developers to interact with Polkadot Hub.\n\nSubWallet now fully supports the [Polkadot Hub TestNet](/polkadot-protocol/smart-contract-basics/networks/#test-networks){target=\\_blank} where developers can deploy and interact with Ethereum-compatible, Solidity smart contracts.\n\nYou can easily view and manage your Paseo native token (PAS) using the Ethereum RPC endpoint (Passet Hub EVM) or the Substrate node RPC endpoint (passet-hub).\n\n??? code \"Polkadot Hub TestNet\"\n You can see support here for Polkadot Hub's TestNet. The **Passet Hub EVM** network uses an ETH RPC endpoint, and the **passet-hub** uses a Substrate endpoint.\n The ETH RPC endpoint will let you send transactions that follow an ETH format, while the Substrate endpoint will follow a Substrate transaction format.\n Note the PAS token, which is the native token of the Polkadot Hub TestNet.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-PAS.webp){: .browser-extension}\n\nTo connect to Polkadot Hub TestNet using SubWallet, follow these steps:\n\n1. Install the [SubWallet browser extension](https://chromewebstore.google.com/detail/subwallet-polkadot-wallet/onhogfjeacnfoofkfgppdlbmlmnplgbn?hl=en){target=\\_blank} and set up your wallet by following the on-screen instructions, or refer to our [step-by-step guide](https://docs.subwallet.app/main/extension-user-guide/getting-started/install-subwallet){target=\\_blank} for assistance.\n\n2. After setting up your wallet, click the List icon at the top left corner of the extension window to open **Settings**.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-01.webp){: .browser-extension}\n\n3. Scroll down and select **Manage networks**.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-02.webp){: .browser-extension}\n\n4. In the Manage network screen, either scroll down or type in the search bar to find the networks. Once done, enable the toggle next to the network name.\n\n ![](/images/smart-contracts/integrations/wallets/subwallet-03.webp){: .browser-extension}\n\n You are now ready to use SubWallet to interact with [Polkadot Hub TestNet](/smart-contracts/connect/#networks-details){target=\\_blank} seamlessly!\n\n![](/images/smart-contracts/integrations/wallets/subwallet-04.webp){: .browser-extension}"} +{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 4, "depth": 3, "title": "Talisman", "anchor": "talisman", "start_char": 4969, "end_char": 6716, "estimated_token_count": 431, "token_estimator": "heuristic-v1", "text": "### Talisman\n\n[Talisman](https://talisman.xyz/){target=\\_blank} is a specialized wallet for the Polkadot ecosystem that supports both Substrate and EVM accounts, making it an excellent choice for Polkadot Hub interactions. Talisman offers a more integrated experience for Polkadot-based chains while still providing Ethereum compatibility.\n\nTo use Talisman with Polkadot Hub TestNet:\n\n1. Install the [Talisman extension](https://talisman.xyz/download){target=\\_blank} and set up your wallet by following the on-screen instructions.\n\n2. Once installed, click on the Talisman icon in your browser extensions and click on the **Settings** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-5.webp){: .browser-extension}\n\n3. Click the button **All settings**.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-6.webp){: .browser-extension}\n\n4. Go to the **Networks & Tokens** section.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-7.webp)\n\n5. Click the **Manage networks** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-8.webp)\n\n6. Click the **+ Add network** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-9.webp)\n\n7. Fill in the form with the required parameters and click the **Add network** button.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-10.webp)\n\n8. After that, you can switch to the Polkadot Hub TestNet by clicking on the network icon and selecting **Polkadot Hub TestNet**.\n\n ![](/images/smart-contracts/integrations/wallets/wallets-11.webp)\n\nAfter selecting the network, Talisman will automatically configure the necessary RPC URL and chain ID for you. You can now use Talisman to interact with the Polkadot Hub TestNet."} +{"page_id": "smart-contracts-integrations-wallets", "page_title": "Wallets for Polkadot Hub", "index": 5, "depth": 2, "title": "Conclusion", "anchor": "conclusion", "start_char": 6716, "end_char": 7340, "estimated_token_count": 100, "token_estimator": "heuristic-v1", "text": "## Conclusion\n\nChoosing the right wallet for Polkadot Hub interactions depends on your specific requirements and familiarity with different interfaces. MetaMask provides a familiar entry point for developers with Ethereum experience, while Talisman offers deeper integration with Polkadot's unique features and native support for both EVM and Substrate accounts. By properly configuring your wallet connection, you gain access to the full spectrum of Polkadot Hub's capabilities.\n\n!!!info\n Remember to always verify network parameters when connecting to ensure a secure and reliable connection to the Polkadot ecosystem."} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 0, "depth": 2, "title": "Introduction", "anchor": "introduction", "start_char": 13, "end_char": 539, "estimated_token_count": 105, "token_estimator": "heuristic-v1", "text": "## Introduction\n\n[Ethers.js](https://docs.ethers.org/v6/){target=\\_blank} is a lightweight library that enables interaction with Ethereum Virtual Machine (EVM)-compatible blockchains through JavaScript. Ethers is widely used as a toolkit to establish connections and read and write blockchain data. This article demonstrates using Ethers.js to interact and deploy smart contracts to Polkadot Hub.\n\nThis guide is intended for developers who are familiar with JavaScript and want to interact with Polkadot Hub using Ethers.js."} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 1, "depth": 2, "title": "Prerequisites", "anchor": "prerequisites", "start_char": 539, "end_char": 895, "estimated_token_count": 110, "token_estimator": "heuristic-v1", "text": "## Prerequisites\n\nBefore getting started, ensure you have the following installed:\n\n- **Node.js**: v22.13.1 or later, check the [Node.js installation guide](https://nodejs.org/en/download/current/){target=\\_blank}.\n- **npm**: v6.13.4 or later (comes bundled with Node.js).\n- **Solidity**: This guide uses Solidity `^0.8.9` for smart contract development."} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 2, "depth": 2, "title": "Project Structure", "anchor": "project-structure", "start_char": 895, "end_char": 1403, "estimated_token_count": 144, "token_estimator": "heuristic-v1", "text": "## Project Structure\n\nThis project organizes contracts, scripts, and compiled artifacts for easy development and deployment.\n\n```text title=\"Ethers.js Polkadot Hub\"\nethers-project\n├── contracts\n│ ├── Storage.sol\n├── scripts\n│ ├── connectToProvider.js\n│ ├── fetchLastBlock.js\n│ ├── compile.js\n│ ├── deploy.js\n│ ├── checkStorage.js\n├── abis\n│ ├── Storage.json\n├── artifacts\n│ ├── Storage.bin\n├── contract-address.json\n├── node_modules/\n├── package.json\n├── package-lock.json\n└── README.md\n```"} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 3, "depth": 2, "title": "Set Up the Project", "anchor": "set-up-the-project", "start_char": 1403, "end_char": 1624, "estimated_token_count": 50, "token_estimator": "heuristic-v1", "text": "## Set Up the Project\n\nTo start working with Ethers.js, create a new folder and initialize your project by running the following commands in your terminal:\n\n```bash\nmkdir ethers-project\ncd ethers-project\nnpm init -y\n```"} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 4, "depth": 2, "title": "Install Dependencies", "anchor": "install-dependencies", "start_char": 1624, "end_char": 2083, "estimated_token_count": 122, "token_estimator": "heuristic-v1", "text": "## Install Dependencies\n\nNext, run the following command to install the Ethers.js library:\n\n```bash\nnpm install ethers\n```\n\nAdd the Solidity compiler so you can generate standard EVM bytecode:\n\n```bash\nnpm install --save-dev solc\n```\n\nThis guide uses `solc` version `0.8.33`.\n\n!!! tip\n The sample scripts use ECMAScript modules. Add `\"type\": \"module\"` to your `package.json` (or rename the files to `.mjs`) so that `node` can run the `import` statements."} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 5, "depth": 2, "title": "Set Up the Ethers.js Provider", "anchor": "set-up-the-ethersjs-provider", "start_char": 2083, "end_char": 4683, "estimated_token_count": 578, "token_estimator": "heuristic-v1", "text": "## Set Up the Ethers.js Provider\n\nA [`Provider`](https://docs.ethers.org/v6/api/providers/#Provider){target=\\_blank} is an abstraction of a connection to the Ethereum network, allowing you to query blockchain data and send transactions. It serves as a bridge between your application and the blockchain.\n\nTo interact with Polkadot Hub, you must set up an Ethers.js provider. This provider connects to a blockchain node, allowing you to query blockchain data and interact with smart contracts. In the root of your project, create a file named `connectToProvider.js` and add the following code:\n\n```js title=\"scripts/connectToProvider.js\"\nconst { JsonRpcProvider } = require('ethers');\n\nconst createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n\n return provider;\n};\n\nconst PROVIDER_RPC = {\n rpc: 'INSERT_RPC_URL',\n chainId: 'INSERT_CHAIN_ID',\n name: 'INSERT_CHAIN_NAME',\n};\n\ncreateProvider(PROVIDER_RPC.rpc, PROVIDER_RPC.chainId, PROVIDER_RPC.name);\n\n```\n\n!!! note\n Replace `INSERT_RPC_URL`, `INSERT_CHAIN_ID`, and `INSERT_CHAIN_NAME` with the appropriate values. For example, to connect to Polkadot Hub TestNet's Ethereum RPC instance, you can use the following parameters:\n\n ```js\n const PROVIDER_RPC = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n name: 'polkadot-hub-testnet'\n };\n ```\n\nTo connect to the provider, execute:\n\n```bash\nnode scripts/connectToProvider.js\n```\n\nWith the provider set up, you can start querying the blockchain. For instance, to fetch the latest block number:\n\n??? code \"fetchLastBlock.js code\"\n\n ```js title=\"scripts/fetchLastBlock.js\"\n const { JsonRpcProvider } = require('ethers');\n\n const createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n\n return provider;\n };\n\n const PROVIDER_RPC = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n name: 'polkadot-hub-testnet',\n };\n\n const main = async () => {\n try {\n const provider = createProvider(\n PROVIDER_RPC.rpc,\n PROVIDER_RPC.chainId,\n PROVIDER_RPC.name,\n );\n const latestBlock = await provider.getBlockNumber();\n console.log(`Latest block: ${latestBlock}`);\n } catch (error) {\n console.error('Error connecting to Polkadot Hub TestNet: ' + error.message);\n }\n };\n\n main();\n\n ```"} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 6, "depth": 2, "title": "Compile Contracts", "anchor": "compile-contracts", "start_char": 4683, "end_char": 5012, "estimated_token_count": 74, "token_estimator": "heuristic-v1", "text": "## Compile Contracts\n\nPolkadot Hub exposes an Ethereum JSON-RPC endpoint, so you can compile Solidity contracts to familiar EVM bytecode with the upstream [`solc`](https://www.npmjs.com/package/solc){target=\\_blank} compiler. The resulting artifacts work with any EVM-compatible toolchain and can be deployed through Ethers.js."} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 7, "depth": 3, "title": "Sample Storage Smart Contract", "anchor": "sample-storage-smart-contract", "start_char": 5012, "end_char": 5837, "estimated_token_count": 171, "token_estimator": "heuristic-v1", "text": "### Sample Storage Smart Contract\n\nThis example demonstrates compiling a `Storage.sol` Solidity contract for deployment to Polkadot Hub. The contract's functionality stores a number and permits users to update it with a new value.\n\n```solidity title=\"contracts/Storage.sol\"\n//SPDX-License-Identifier: MIT\n\n// Solidity files have to start with this pragma.\n// It will be used by the Solidity compiler to validate its version.\npragma solidity ^0.8.9;\n\ncontract Storage {\n // Public state variable to store a number\n uint256 public storedNumber;\n\n /**\n * Updates the stored number.\n *\n * The `public` modifier allows anyone to call this function.\n *\n * @param _newNumber - The new value to store.\n */\n function setNumber(uint256 _newNumber) public {\n storedNumber = _newNumber;\n }\n}\n```"} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 8, "depth": 3, "title": "Compile the Smart Contract", "anchor": "compile-the-smart-contract", "start_char": 5837, "end_char": 9394, "estimated_token_count": 810, "token_estimator": "heuristic-v1", "text": "### Compile the Smart Contract\n\nTo compile this contract, use the following script:\n\n```js title=\"scripts/compile.js\"\nconst solc = require('solc');\nconst { readFileSync, writeFileSync, mkdirSync, existsSync } = require('fs');\nconst { basename, join } = require('path');\n\nconst ensureDir = (dirPath) => {\n if (!existsSync(dirPath)) {\n mkdirSync(dirPath, { recursive: true });\n }\n};\n\nconst compileContract = (solidityFilePath, abiDir, artifactsDir) => {\n try {\n // Read the Solidity file\n const source = readFileSync(solidityFilePath, 'utf8');\n const fileName = basename(solidityFilePath);\n \n // Construct the input object for the Solidity compiler\n const input = {\n language: 'Solidity',\n sources: {\n [fileName]: {\n content: source,\n },\n },\n settings: {\n outputSelection: {\n '*': {\n '*': ['abi', 'evm.bytecode'],\n },\n },\n },\n };\n \n console.log(`Compiling contract: ${fileName}...`);\n \n // Compile the contract\n const output = JSON.parse(solc.compile(JSON.stringify(input)));\n \n // Check for errors\n if (output.errors) {\n const errors = output.errors.filter(error => error.severity === 'error');\n if (errors.length > 0) {\n console.error('Compilation errors:');\n errors.forEach(err => console.error(err.formattedMessage));\n return;\n }\n // Show warnings\n const warnings = output.errors.filter(error => error.severity === 'warning');\n warnings.forEach(warn => console.warn(warn.formattedMessage));\n }\n \n // Ensure output directories exist\n ensureDir(abiDir);\n ensureDir(artifactsDir);\n\n // Process compiled contracts\n for (const [sourceFile, contracts] of Object.entries(output.contracts)) {\n for (const [contractName, contract] of Object.entries(contracts)) {\n console.log(`Compiled contract: ${contractName}`);\n \n // Write the ABI\n const abiPath = join(abiDir, `${contractName}.json`);\n writeFileSync(abiPath, JSON.stringify(contract.abi, null, 2));\n console.log(`ABI saved to ${abiPath}`);\n \n // Write the bytecode\n const bytecodePath = join(artifactsDir, `${contractName}.bin`);\n writeFileSync(bytecodePath, contract.evm.bytecode.object);\n console.log(`Bytecode saved to ${bytecodePath}`);\n }\n }\n } catch (error) {\n console.error('Error compiling contracts:', error);\n }\n};\n\nconst solidityFilePath = join(__dirname, '../contracts/Storage.sol');\nconst abiDir = join(__dirname, '../abis');\nconst artifactsDir = join(__dirname, '../artifacts');\n\ncompileContract(solidityFilePath, abiDir, artifactsDir);\n```\n\n!!! note \n The script above is tailored to the `Storage.sol` contract. It can be adjusted for other contracts by changing the file name or modifying the ABI and bytecode paths.\n\nThe ABI (Application Binary Interface) is a JSON representation of your contract's functions, events, and their parameters. It serves as the interface between your JavaScript code and the deployed smart contract, allowing your application to know how to format function calls and interpret returned data.\n\nExecute the script above by running:\n\n```bash\nnode scripts/compile.js\n```\n\nAfter executing the script, the Solidity contract is compiled into standard EVM bytecode. The ABI and bytecode are saved into files with `.json` and `.bin` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section."} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 9, "depth": 2, "title": "Deploy the Compiled Contract", "anchor": "deploy-the-compiled-contract", "start_char": 9394, "end_char": 17247, "estimated_token_count": 1672, "token_estimator": "heuristic-v1", "text": "## Deploy the Compiled Contract\n\nTo deploy your compiled contract to Polkadot Hub, you'll need a wallet with a private key to sign the deployment transaction.\n\nYou can create a `deploy.js` script in the root of your project to achieve this. The deployment script can be divided into key components:\n\n1. Set up the required imports and utilities:\n\n ```js title=\"scripts/deploy.js\"\n const { writeFileSync, existsSync, readFileSync } = require('fs');\n const { join } = require('path');\n const { ethers, JsonRpcProvider } = require('ethers');\n\n ```\n\n2. Create a provider to connect to Polkadot Hub:\n\n ```js title=\"scripts/deploy.js\"\n\n // Creates a provider with specified RPC URL and chain details\n const createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n return provider;\n };\n ```\n \n3. Set up functions to read contract artifacts:\n\n ```js title=\"scripts/deploy.js\"\n // Reads and parses the ABI file for a given contract\n const getAbi = (contractName) => {\n try {\n const abiPath = join(artifactsDir, `${contractName}.json`);\n return JSON.parse(readFileSync(abiPath, 'utf8'));\n } catch (error) {\n console.error(\n `Could not find ABI for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n\n // Reads the compiled bytecode for a given contract\n const getByteCode = (contractName) => {\n try {\n const bytecodePath = join(artifactsDir, `${contractName}.bin`);\n const bytecode = readFileSync(bytecodePath, 'utf8').trim();\n // Add 0x prefix if not present\n return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`;\n } catch (error) {\n console.error(\n `Could not find bytecode for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n\n ```\n\n4. Create the main deployment function:\n\n ```js title=\"scripts/deploy.js\"\n const deployContract = async (contractName, mnemonic, providerConfig) => {\n console.log(`Deploying ${contractName}...`);\n try {\n // Step 1: Set up provider and wallet\n const provider = createProvider(\n providerConfig.rpc,\n providerConfig.chainId,\n providerConfig.name,\n );\n const walletMnemonic = ethers.Wallet.fromPhrase(mnemonic);\n const wallet = walletMnemonic.connect(provider);\n\n // Step 2: Create and deploy the contract\n const factory = new ethers.ContractFactory(\n getAbi(contractName),\n getByteCode(contractName),\n wallet,\n );\n const contract = await factory.deploy();\n await contract.waitForDeployment();\n\n // Step 3: Save deployment information\n const address = await contract.getAddress();\n console.log(`Contract ${contractName} deployed at: ${address}`);\n\n const addressesFile = join(scriptsDir, 'contract-address.json');\n const addresses = existsSync(addressesFile)\n ? JSON.parse(readFileSync(addressesFile, 'utf8'))\n : {};\n\n addresses[contractName] = address;\n writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8');\n } catch (error) {\n console.error(`Failed to deploy contract ${contractName}:`, error);\n }\n };\n ```\n\n5. Configure and execute the deployment:\n\n ```js title=\"scripts/deploy.js\"\n const providerConfig = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready\n chainId: 420420422,\n name: 'polkadot-hub-testnet',\n };\n\n const mnemonic = 'INSERT_MNEMONIC';\n\n deployContract('Storage', mnemonic, providerConfig);\n ```\n\n !!! note\n A mnemonic (seed phrase) is a series of words that can generate multiple private keys and their corresponding addresses. It's used here to derive the wallet that will sign and pay for the deployment transaction. **Always keep your mnemonic secure and never share it publicly**.\n\n Ensure to replace the `INSERT_MNEMONIC` placeholder with your actual mnemonic.\n\n??? code \"View complete script\"\n\n ```js title=\"scripts/deploy.js\"\n const { writeFileSync, existsSync, readFileSync } = require('fs');\n const { join } = require('path');\n const { ethers, JsonRpcProvider } = require('ethers');\n\n const scriptsDir = __dirname;\n const artifactsDir = join(__dirname, '../contracts');\n\n // Creates a provider with specified RPC URL and chain details\n const createProvider = (rpcUrl, chainId, chainName) => {\n const provider = new JsonRpcProvider(rpcUrl, {\n chainId: chainId,\n name: chainName,\n });\n return provider;\n };\n\n // Reads and parses the ABI file for a given contract\n const getAbi = (contractName) => {\n try {\n const abiPath = join(artifactsDir, `${contractName}.json`);\n return JSON.parse(readFileSync(abiPath, 'utf8'));\n } catch (error) {\n console.error(\n `Could not find ABI for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n\n // Reads the compiled bytecode for a given contract\n const getByteCode = (contractName) => {\n try {\n const bytecodePath = join(artifactsDir, `${contractName}.bin`);\n const bytecode = readFileSync(bytecodePath, 'utf8').trim();\n // Add 0x prefix if not present\n return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`;\n } catch (error) {\n console.error(\n `Could not find bytecode for contract ${contractName}:`,\n error.message,\n );\n throw error;\n }\n };\n\n const deployContract = async (contractName, mnemonic, providerConfig) => {\n console.log(`Deploying ${contractName}...`);\n try {\n // Step 1: Set up provider and wallet\n const provider = createProvider(\n providerConfig.rpc,\n providerConfig.chainId,\n providerConfig.name,\n );\n const walletMnemonic = ethers.Wallet.fromPhrase(mnemonic);\n const wallet = walletMnemonic.connect(provider);\n\n // Step 2: Create and deploy the contract\n const factory = new ethers.ContractFactory(\n getAbi(contractName),\n getByteCode(contractName),\n wallet,\n );\n const contract = await factory.deploy();\n await contract.waitForDeployment();\n\n // Step 3: Save deployment information\n const address = await contract.getAddress();\n console.log(`Contract ${contractName} deployed at: ${address}`);\n\n const addressesFile = join(scriptsDir, 'contract-address.json');\n const addresses = existsSync(addressesFile)\n ? JSON.parse(readFileSync(addressesFile, 'utf8'))\n : {};\n\n addresses[contractName] = address;\n writeFileSync(addressesFile, JSON.stringify(addresses, null, 2), 'utf8');\n } catch (error) {\n console.error(`Failed to deploy contract ${contractName}:`, error);\n }\n };\n\n const providerConfig = {\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io', //TODO: replace to `https://services.polkadothub-rpc.com/testnet` when ready\n chainId: 420420422,\n name: 'polkadot-hub-testnet',\n };\n\n const mnemonic = 'INSERT_MNEMONIC';\n\n deployContract('Storage', mnemonic, providerConfig);\n ```\n\nTo run the script, execute the following command:\n\n```bash\nnode scripts/deploy.js\n```\n\nAfter running this script, your contract will be deployed to Polkadot Hub, and its address will be saved in `contract-address.json` within your project directory. You can use this address for future contract interactions."} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 10, "depth": 2, "title": "Interact with the Contract", "anchor": "interact-with-the-contract", "start_char": 17247, "end_char": 20687, "estimated_token_count": 751, "token_estimator": "heuristic-v1", "text": "## Interact with the Contract\n\nOnce the contract is deployed, you can interact with it by calling its functions. For example, to set a number, read it and then modify that number by its double, you can create a file named `checkStorage.js` in the root of your project and add the following code:\n\n```js title=\"scripts/checkStorage.js\"\nconst { ethers } = require('ethers');\nconst { readFileSync } = require('fs');\nconst { join } = require('path');\n\nconst artifactsDir = join(__dirname, '../contracts');\n\nconst createProvider = (providerConfig) => {\n return new ethers.JsonRpcProvider(providerConfig.rpc, {\n chainId: providerConfig.chainId,\n name: providerConfig.name,\n });\n};\n\nconst createWallet = (mnemonic, provider) => {\n return ethers.Wallet.fromPhrase(mnemonic).connect(provider);\n};\n\nconst loadContractAbi = (contractName, directory = artifactsDir) => {\n const contractPath = join(directory, `${contractName}.json`);\n const contractJson = JSON.parse(readFileSync(contractPath, 'utf8'));\n return contractJson.abi || contractJson; // Depending on JSON structure\n};\n\nconst createContract = (contractAddress, abi, wallet) => {\n return new ethers.Contract(contractAddress, abi, wallet);\n};\n\nconst interactWithStorageContract = async (\n contractName,\n contractAddress,\n mnemonic,\n providerConfig,\n numberToSet,\n) => {\n try {\n console.log(`Setting new number in Storage contract: ${numberToSet}`);\n\n // Create provider and wallet\n const provider = createProvider(providerConfig);\n const wallet = createWallet(mnemonic, provider);\n\n // Load the contract ABI and create the contract instance\n const abi = loadContractAbi(contractName);\n const contract = createContract(contractAddress, abi, wallet);\n\n // Send a transaction to set the stored number\n const tx1 = await contract.setNumber(numberToSet);\n await tx1.wait(); // Wait for the transaction to be mined\n console.log(`Number successfully set to ${numberToSet}`);\n\n // Retrieve the updated number\n const storedNumber = await contract.storedNumber();\n console.log(`Retrieved stored number:`, storedNumber.toString());\n\n // Send a transaction to set the stored number\n const tx2 = await contract.setNumber(numberToSet * 2);\n await tx2.wait(); // Wait for the transaction to be mined\n console.log(`Number successfully set to ${numberToSet * 2}`);\n\n // Retrieve the updated number\n const updatedNumber = await contract.storedNumber();\n console.log(`Retrieved stored number:`, updatedNumber.toString());\n } catch (error) {\n console.error('Error interacting with Storage contract:', error.message);\n }\n};\n\nconst providerConfig = {\n name: 'asset-hub-smart-contracts',\n rpc: 'https://testnet-passet-hub-eth-rpc.polkadot.io',\n chainId: 420420422,\n};\n\nconst mnemonic = 'INSERT_MNEMONIC'\nconst contractName = 'Storage';\nconst contractAddress = 'INSERT_CONTRACT_ADDRESS'\nconst newNumber = 42;\n\ninteractWithStorageContract(\n contractName,\n contractAddress,\n mnemonic,\n providerConfig,\n newNumber,\n);\n```\n\nEnsure you replace the `INSERT_MNEMONIC` and `INSERT_CONTRACT_ADDRESS` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. The script prints the balance for `ADDRESS_TO_CHECK` before it writes and doubles the stored value, so pick any account you want to monitor.\n\nTo interact with the contract, run:\n\n```bash\nnode scripts/checkStorage.js\n```"} +{"page_id": "smart-contracts-libraries-ethers-js", "page_title": "Deploy Contracts to Polkadot Hub with Ethers.js", "index": 11, "depth": 2, "title": "Where to Go Next", "anchor": "where-to-go-next", "start_char": 20687, "end_char": 21292, "estimated_token_count": 155, "token_estimator": "heuristic-v1", "text": "## Where to Go Next\n\nNow that you have the foundational knowledge to use Ethers.js with Polkadot Hub, you can:\n\n- **Dive into Ethers.js utilities**: Discover additional Ethers.js features, such as wallet management, signing messages, etc.\n- **Implement batch transactions**: Use Ethers.js to execute batch transactions for efficient multi-step contract interactions.\n- **Build scalable applications**: Combine Ethers.js with frameworks like [`Next.js`](https://nextjs.org/docs){target=\\_blank} or [`Node.js`](https://nodejs.org/en){target=\\_blank} to create full-stack decentralized applications (dApps)."} {"page_id": "smart-contracts-libraries-viem", "page_title": "viem for Polkadot Hub Smart Contracts", "index": 0, "depth": 2, "title": "Introduction", "anchor": "introduction", "start_char": 178, "end_char": 455, "estimated_token_count": 56, "token_estimator": "heuristic-v1", "text": "## Introduction\n\n[viem](https://viem.sh/){target=\\_blank} is a lightweight TypeScript library designed for interacting with Ethereum-compatible blockchains. This comprehensive guide will walk you through using viem to interact with and deploy smart contracts to Polkadot Hub."} {"page_id": "smart-contracts-libraries-viem", "page_title": "viem for Polkadot Hub Smart Contracts", "index": 1, "depth": 2, "title": "Prerequisites", "anchor": "prerequisites", "start_char": 455, "end_char": 811, "estimated_token_count": 110, "token_estimator": "heuristic-v1", "text": "## Prerequisites\n\nBefore getting started, ensure you have the following installed:\n\n- **Node.js**: v22.13.1 or later, check the [Node.js installation guide](https://nodejs.org/en/download/current/){target=\\_blank}.\n- **npm**: v6.13.4 or later (comes bundled with Node.js).\n- **Solidity**: This guide uses Solidity `^0.8.9` for smart contract development."} {"page_id": "smart-contracts-libraries-viem", "page_title": "viem for Polkadot Hub Smart Contracts", "index": 2, "depth": 2, "title": "Project Structure", "anchor": "project-structure", "start_char": 811, "end_char": 1231, "estimated_token_count": 119, "token_estimator": "heuristic-v1", "text": "## Project Structure\n\nThis project organizes contracts, scripts, and compiled artifacts for easy development and deployment.\n\n```text\nviem-project/\n├── package.json\n├── tsconfig.json\n├── src/\n│ ├── chainConfig.ts\n│ ├── createClient.ts\n│ ├── createWallet.ts\n│ ├── compile.ts\n│ ├── deploy.ts\n│ └── interact.ts\n├── contracts/\n│ └── Storage.sol\n└── artifacts/\n ├── Storage.json\n └── Storage.polkavm\n```"} diff --git a/smart-contracts/libraries/ethers-js.md b/smart-contracts/libraries/ethers-js.md index 0afab9ce6..374e5f915 100644 --- a/smart-contracts/libraries/ethers-js.md +++ b/smart-contracts/libraries/ethers-js.md @@ -6,8 +6,6 @@ categories: Smart Contracts, Tooling # Ethers.js ---8<-- 'text/smart-contracts/polkaVM-warning.md' - ## Introduction [Ethers.js](https://docs.ethers.org/v6/){target=\_blank} is a lightweight library that enables interaction with Ethereum Virtual Machine (EVM)-compatible blockchains through JavaScript. Ethers is widely used as a toolkit to establish connections and read and write blockchain data. This article demonstrates using Ethers.js to interact and deploy smart contracts to Polkadot Hub. @@ -39,7 +37,7 @@ ethers-project ├── abis │ ├── Storage.json ├── artifacts -│ ├── Storage.polkavm +│ ├── Storage.bin ├── contract-address.json ├── node_modules/ ├── package.json @@ -65,6 +63,17 @@ Next, run the following command to install the Ethers.js library: npm install ethers ``` +Add the Solidity compiler so you can generate standard EVM bytecode: + +```bash +npm install --save-dev solc +``` + +This guide uses `solc` version `{{ dependencies.javascript_packages.solc.version }}`. + +!!! tip + The sample scripts use ECMAScript modules. Add `"type": "module"` to your `package.json` (or rename the files to `.mjs`) so that `node` can run the `import` statements. + ## Set Up the Ethers.js Provider A [`Provider`](https://docs.ethers.org/v6/api/providers/#Provider){target=\_blank} is an abstraction of a connection to the Ethereum network, allowing you to query blockchain data and send transactions. It serves as a bridge between your application and the blockchain. @@ -89,12 +98,12 @@ To interact with Polkadot Hub, you must set up an Ethers.js provider. This provi To connect to the provider, execute: ```bash -node connectToProvider +node scripts/connectToProvider.js ``` With the provider set up, you can start querying the blockchain. For instance, to fetch the latest block number: -??? code "Fetch Last Block code" +??? code "fetchLastBlock.js code" ```js title="scripts/fetchLastBlock.js" --8<-- 'code/smart-contracts/libraries/ethers-js/fetchLastBlock.js' @@ -102,19 +111,7 @@ With the provider set up, you can start querying the blockchain. For instance, t ## Compile Contracts ---8<-- 'text/smart-contracts/code-size.md' - -The `revive` compiler transforms Solidity smart contracts into [PolkaVM](/smart-contracts/overview/#native-smart-contracts){target=\_blank} bytecode for deployment on Polkadot Hub. Revive's Ethereum RPC interface allows you to use familiar tools like Ethers.js and MetaMask to interact with contracts. - -### Install the Revive Library - -The [`@parity/resolc`](https://www.npmjs.com/package/@parity/resolc){target=\_blank} library will compile your Solidity code for deployment on Polkadot Hub. Run the following command in your terminal to install the library: - -```bash -npm install --save-dev @parity/resolc -``` - -This guide uses `@parity/resolc` version `{{ dependencies.javascript_packages.resolc.version }}`. +Polkadot Hub exposes an Ethereum JSON-RPC endpoint, so you can compile Solidity contracts to familiar EVM bytecode with the upstream [`solc`](https://www.npmjs.com/package/solc){target=\_blank} compiler. The resulting artifacts work with any EVM-compatible toolchain and can be deployed through Ethers.js. ### Sample Storage Smart Contract @@ -140,10 +137,10 @@ The ABI (Application Binary Interface) is a JSON representation of your contract Execute the script above by running: ```bash -node compile +node scripts/compile.js ``` -After executing the script, the Solidity contract will be compiled into the required PolkaVM bytecode format. The ABI and bytecode will be saved into files with `.json` and `.polkavm` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. +After executing the script, the Solidity contract is compiled into standard EVM bytecode. The ABI and bytecode are saved into files with `.json` and `.bin` extensions, respectively. You can now proceed with deploying the contract to Polkadot Hub, as outlined in the next section. ## Deploy the Compiled Contract @@ -154,7 +151,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. 1. Set up the required imports and utilities: ```js title="scripts/deploy.js" - --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:1:6' + --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:1:4' ``` 2. Create a provider to connect to Polkadot Hub: @@ -166,19 +163,19 @@ You can create a `deploy.js` script in the root of your project to achieve this. 3. Set up functions to read contract artifacts: ```js title="scripts/deploy.js" - --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:17:48' + --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:17:46' ``` 4. Create the main deployment function: ```js title="scripts/deploy.js" - --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:49:85' + --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:47:82' ``` 5. Configure and execute the deployment: ```js title="scripts/deploy.js" - --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:87:95' + --8<-- 'code/smart-contracts/libraries/ethers-js/deploy.js:84:92' ``` !!! note @@ -195,7 +192,7 @@ You can create a `deploy.js` script in the root of your project to achieve this. To run the script, execute the following command: ```bash -node deploy +node scripts/deploy.js ``` After running this script, your contract will be deployed to Polkadot Hub, and its address will be saved in `contract-address.json` within your project directory. You can use this address for future contract interactions. @@ -208,12 +205,12 @@ Once the contract is deployed, you can interact with it by calling its functions --8<-- 'code/smart-contracts/libraries/ethers-js/checkStorage.js' ``` -Ensure you replace the `INSERT_MNEMONIC`, `INSERT_CONTRACT_ADDRESS`, and `INSERT_ADDRESS_TO_CHECK` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. +Ensure you replace the `INSERT_MNEMONIC` and `INSERT_CONTRACT_ADDRESS` placeholders with actual values. Also, ensure the contract ABI file (`Storage.json`) is correctly referenced. The script prints the balance for `ADDRESS_TO_CHECK` before it writes and doubles the stored value, so pick any account you want to monitor. To interact with the contract, run: ```bash -node checkStorage +node scripts/checkStorage.js ``` ## Where to Go Next diff --git a/variables.yml b/variables.yml index 3886dd564..f173ee4cf 100644 --- a/variables.yml +++ b/variables.yml @@ -71,6 +71,9 @@ dependencies: resolc: name: '@resolc' version: 0.2.0 + solc: + name: 'solc' + version: 0.8.33 web3_js: name: '@web3js' version: 4.16.0