Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 107 additions & 95 deletions .ai/categories/smart-contracts.md

Large diffs are not rendered by default.

202 changes: 107 additions & 95 deletions .ai/categories/tooling.md

Large diffs are not rendered by default.

202 changes: 107 additions & 95 deletions .ai/pages/smart-contracts-libraries-ethers-js.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions .ai/pages/text-smart-contracts-code-size.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
url: https://docs.polkadot.com/text/smart-contracts/code-size/
---

!!! info "Mind the contract size"
Polkadot Hub enforces Ethereum's 24 KiB contract-size limit (EIP-170) for EVM deployments. Keep contracts modular, share logic through libraries, and make use of compiler optimizations so your bytecode stays within the limit.
37 changes: 26 additions & 11 deletions .ai/site-index.json
Original file line number Diff line number Diff line change
Expand Up @@ -8477,7 +8477,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,
Expand Down Expand Up @@ -8514,11 +8514,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",
Expand Down Expand Up @@ -8546,12 +8541,12 @@
}
],
"stats": {
"chars": 20457,
"words": 2333,
"headings": 13,
"estimated_token_count_total": 4474
"chars": 21133,
"words": 2401,
"headings": 12,
"estimated_token_count_total": 4687
},
"hash": "sha256:c74a28d8d62369591c5734535136508db3d1f7380e486fd214f98d433cafd6e7",
"hash": "sha256:ea39f6aab99e80c378fbf7c48030d274e13c340e23465d40d4cfb33c9f0d0f2f",
"token_estimator": "heuristic-v1"
},
{
Expand Down Expand Up @@ -9056,5 +9051,25 @@
},
"hash": "sha256:a40e3f34f70db22bfe39e40d68dc5a53a726ce47cb73b602d8605355c61ffd22",
"token_estimator": "heuristic-v1"
},
{
"id": "text-smart-contracts-code-size",
"title": "text-smart-contracts-code-size",
"slug": "text-smart-contracts-code-size",
"categories": [
"Uncategorized"
],
"raw_md_url": "https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.ai/pages/text-smart-contracts-code-size.md",
"html_url": "https://docs.polkadot.com/text/smart-contracts/code-size/",
"preview": "!!! info \"Mind the contract size\" Polkadot Hub enforces Ethereum's 24 KiB contract-size limit (EIP-170) for EVM deployments. Keep contracts modular, share logic through libraries, and make use of compiler optimizations so your bytecode stays within the limit.",
"outline": [],
"stats": {
"chars": 264,
"words": 40,
"headings": 0,
"estimated_token_count_total": 0
},
"hash": "sha256:e6c2ffab1e1d9d06f6e209f37e1c829abd6be41fa7c5350d7dcc858745d4e011",
"token_estimator": "heuristic-v1"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -69,9 +71,9 @@ const providerConfig = {
chainId: 420420422,
};

const mnemonic = 'INSERT_MNEMONIC';
const mnemonic = 'evoke moment pluck misery cheese boy era fresh useful frame resemble cinnamon';
const contractName = 'Storage';
const contractAddress = 'INSERT_CONTRACT_ADDRESS';
const contractAddress = '0x83e43892a98f924706E9DB7917244897dC8b8126';
const newNumber = 42;

interactWithStorageContract(
Expand All @@ -80,4 +82,4 @@ interactWithStorageContract(
mnemonic,
providerConfig,
newNumber,
);
);
81 changes: 58 additions & 23 deletions .snippets/code/smart-contracts/libraries/ethers-js/compile.js
Original file line number Diff line number Diff line change
@@ -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}`);
}
}
Expand All @@ -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);
31 changes: 14 additions & 17 deletions .snippets/code/smart-contracts/libraries/ethers-js/deploy.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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}:`,
Expand All @@ -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}:`,
Expand All @@ -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(
Expand All @@ -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) {
Expand All @@ -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';
const mnemonic = 'evoke moment pluck misery cheese boy era fresh useful frame resemble cinnamon';

deployContract('Storage', mnemonic, providerConfig);
deployContract('Storage', mnemonic, providerConfig);
17 changes: 17 additions & 0 deletions code/smart-contracts/libraries/ethers-js/Storage.sol
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading
Loading