Skip to content
Merged
9,746 changes: 4,965 additions & 4,781 deletions .ai/categories/smart-contracts.md

Large diffs are not rendered by default.

1,212 changes: 698 additions & 514 deletions .ai/categories/tooling.md

Large diffs are not rendered by default.

528 changes: 356 additions & 172 deletions .ai/pages/smart-contracts-libraries-web3-js.md

Large diffs are not rendered by default.

28 changes: 19 additions & 9 deletions .ai/site-index.json
Original file line number Diff line number Diff line change
Expand Up @@ -8720,15 +8720,15 @@
},
{
"id": "smart-contracts-libraries-web3-js",
"title": "Web3.js",
"title": "Deploy Contracts to Polkadot Hub with Web3.js",
"slug": "smart-contracts-libraries-web3-js",
"categories": [
"Smart Contracts",
"Tooling"
],
"raw_md_url": "https://raw.githubusercontent.com/polkadot-developers/polkadot-docs/master/.ai/pages/smart-contracts-libraries-web3-js.md",
"html_url": "https://docs.polkadot.com/smart-contracts/libraries/web3-js/",
"preview": "!!! smartcontract \"PolkaVM Preview Release\" PolkaVM smart contracts with Ethereum compatibility are in **early-stage development and may be unstable or incomplete**. !!! warning Web3.js has been [sunset](https://blog.chainsafe.io/web3-js-sunset/){target=\\_blank}. You can find guides on using [Ethers.js](/smart-contracts/libraries/ethers-js/){target=\\_blank} and [viem](/smart-contracts/libraries/viem/){target=\\_blank} in the Libraries section.",
"preview": "!!! warning Web3.js has been [sunset](https://blog.chainsafe.io/web3-js-sunset/){target=\\_blank}. You can find guides on using [Ethers.js](/smart-contracts/libraries/ethers-js/){target=\\_blank} and [viem](/smart-contracts/libraries/viem/){target=\\_blank} in the Libraries section.",
"outline": [
{
"depth": 2,
Expand Down Expand Up @@ -8765,10 +8765,20 @@
"title": "Compile Contracts",
"anchor": "compile-contracts"
},
{
"depth": 3,
"title": "Sample Storage Smart Contract",
"anchor": "sample-storage-smart-contract"
},
{
"depth": 3,
"title": "Compile the Smart Contract",
"anchor": "compile-the-smart-contract"
},
{
"depth": 2,
"title": "Contract Deployment",
"anchor": "contract-deployment"
"title": "Deploy the Compiled Contract",
"anchor": "deploy-the-compiled-contract"
},
{
"depth": 2,
Expand All @@ -8782,12 +8792,12 @@
}
],
"stats": {
"chars": 13266,
"words": 1579,
"headings": 10,
"estimated_token_count_total": 3035
"chars": 20552,
"words": 2326,
"headings": 12,
"estimated_token_count_total": 4656
},
"hash": "sha256:f0d36333d0d3afff7f6374a61d0f6d1fb878c9ef4c4e4c24447745661dbe59d0",
"hash": "sha256:4b94081bbb31a17d62c515b617c846ce87ff82d7af5e8b732326e13ed7c767d5",
"token_estimator": "heuristic-v1"
},
{
Expand Down
83 changes: 59 additions & 24 deletions .snippets/code/smart-contracts/libraries/web3-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 @@ -40,7 +74,8 @@ const compileContract = async (solidityFilePath, outputDir) => {
}
};

const solidityFilePath = './Storage.sol';
const outputDir = '.';
const solidityFilePath = join(__dirname, '../contracts/Storage.sol');
const abiDir = join(__dirname, '../abis');
const artifactsDir = join(__dirname, '../artifacts');

compileContract(solidityFilePath, outputDir);
compileContract(solidityFilePath, abiDir, artifactsDir);
104 changes: 60 additions & 44 deletions .snippets/code/smart-contracts/libraries/web3-js/deploy.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,98 @@
import { readFileSync } from 'fs';
import { Web3 } from 'web3';
const { writeFileSync, existsSync, readFileSync } = require('fs');
const { join } = require('path');
const { Web3 } = require('web3');

const scriptsDir = __dirname;
const abisDir = join(__dirname, '../abis');
const artifactsDir = join(__dirname, '../artifacts');

const createProvider = (rpcUrl, chainId, chainName) => {
const web3 = new Web3(rpcUrl);
return web3;
};

const getAbi = (contractName) => {
try {
return JSON.parse(readFileSync(`${contractName}.json`), 'utf8');
const abiPath = join(abisDir, `${contractName}.json`);
return JSON.parse(readFileSync(abiPath, 'utf8'));
} catch (error) {
console.error(
`❌ Could not find ABI for contract ${contractName}:`,
error.message
`Could not find ABI for contract ${contractName}:`,
error.message,
);
throw error;
}
};

const getByteCode = (contractName) => {
try {
return `0x${readFileSync(`${contractName}.polkavm`).toString('hex')}`;
const bytecodePath = join(artifactsDir, `${contractName}.bin`);
const bytecode = readFileSync(bytecodePath, 'utf8').trim();
return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`;
} catch (error) {
console.error(
`❌ Could not find bytecode for contract ${contractName}:`,
error.message
`Could not find bytecode for contract ${contractName}:`,
error.message,
);
throw error;
}
};

export const deploy = async (config) => {
const deployContract = async (contractName, privateKey, providerConfig) => {
console.log(`Deploying ${contractName}...`);
try {
// Initialize Web3 with RPC URL
const web3 = new Web3(config.rpcUrl);
const web3 = createProvider(
providerConfig.rpc,
providerConfig.chainId,
providerConfig.name,
);

// Prepare account
const account = web3.eth.accounts.privateKeyToAccount(config.privateKey);
const formattedPrivateKey = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
const account = web3.eth.accounts.privateKeyToAccount(formattedPrivateKey);
web3.eth.accounts.wallet.add(account);
web3.eth.defaultAccount = account.address;

// Load abi
const abi = getAbi('Storage');

// Create contract instance
const abi = getAbi(contractName);
const bytecode = getByteCode(contractName);
const contract = new web3.eth.Contract(abi);

// Prepare deployment
const deployTransaction = contract.deploy({
data: getByteCode('Storage'),
arguments: [], // Add constructor arguments if needed
const deployTx = contract.deploy({
data: bytecode,
});

// Estimate gas
const gasEstimate = await deployTransaction.estimateGas({
from: account.address,
});

// Get current gas price
const gas = await deployTx.estimateGas();
const gasPrice = await web3.eth.getGasPrice();

// Send deployment transaction
const deployedContract = await deployTransaction.send({
console.log(`Estimated gas: ${gas}`);
console.log(`Gas price: ${web3.utils.fromWei(gasPrice, 'gwei')} gwei`);

const deployedContract = await deployTx.send({
from: account.address,
gas: gasEstimate,
gas: gas,
gasPrice: gasPrice,
});

// Log and return contract details
console.log(`Contract deployed at: ${deployedContract.options.address}`);
return deployedContract;
const address = deployedContract.options.address;
console.log(`Contract ${contractName} deployed at: ${address}`);

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) {
console.error('Deployment failed:', error);
throw error;
console.error(`Failed to deploy contract ${contractName}:`, error);
}
};

// Example usage
const deploymentConfig = {
rpcUrl: 'INSERT_RPC_URL',
privateKey: 'INSERT_PRIVATE_KEY',
contractName: 'INSERT_CONTRACT_NAME',
const providerConfig = {
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',
};

deploy(deploymentConfig)
.then((contract) => console.log('Deployment successful'))
.catch((error) => console.error('Deployment error'));
const privateKey = 'INSERT_PRIVATE_KEY';

deployContract('Storage', privateKey, providerConfig);

37 changes: 13 additions & 24 deletions .snippets/code/smart-contracts/libraries/web3-js/updateStorage.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,49 @@
import { readFileSync } from 'fs';
import { Web3 } from 'web3';
const { readFileSync } = require('fs');
const { join } = require('path');
const { Web3 } = require('web3');

const abisDir = join(__dirname, '../abis');

const getAbi = (contractName) => {
try {
return JSON.parse(readFileSync(`${contractName}.json`), 'utf8');
const abiPath = join(abisDir, `${contractName}.json`);
return JSON.parse(readFileSync(abiPath, 'utf8'));
} catch (error) {
console.error(
`❌ Could not find ABI for contract ${contractName}:`,
error.message
`Could not find ABI for contract ${contractName}:`,
error.message,
);
throw error;
}
};

const updateStorage = async (config) => {
try {
// Initialize Web3 with RPC URL
const web3 = new Web3(config.rpcUrl);

// Prepare account
const account = web3.eth.accounts.privateKeyToAccount(config.privateKey);
const formattedPrivateKey = config.privateKey.startsWith('0x') ? config.privateKey : `0x${config.privateKey}`;
const account = web3.eth.accounts.privateKeyToAccount(formattedPrivateKey);
web3.eth.accounts.wallet.add(account);

// Load abi
const abi = getAbi('Storage');

// Create contract instance
const contract = new web3.eth.Contract(abi, config.contractAddress);

// Get initial value
const initialValue = await contract.methods.storedNumber().call();
console.log('Current stored value:', initialValue);

// Prepare transaction
const updateTransaction = contract.methods.setNumber(1);

// Estimate gas
const gasEstimate = await updateTransaction.estimateGas({
from: account.address,
});

// Get current gas price
const gasPrice = await web3.eth.getGasPrice();

// Send update transaction
const receipt = await updateTransaction.send({
from: account.address,
gas: gasEstimate,
gasPrice: gasPrice,
});

// Log transaction details
console.log(`Transaction hash: ${receipt.transactionHash}`);

// Get updated value
const newValue = await contract.methods.storedNumber().call();
console.log('New stored value:', newValue);

Expand All @@ -64,13 +54,12 @@ const updateStorage = async (config) => {
}
};

// Example usage
const config = {
rpcUrl: 'INSERT_RPC_URL',
rpcUrl: 'https://testnet-passet-hub-eth-rpc.polkadot.io',
privateKey: 'INSERT_PRIVATE_KEY',
contractAddress: 'INSERT_CONTRACT_ADDRESS',
};

updateStorage(config)
.then((receipt) => console.log('Update successful'))
.catch((error) => console.error('Update error'));
.catch((error) => console.error('Update error'));
Loading
Loading